/*
 *
 *    Copyright (c) 2021 Project CHIP Authors
 *    All rights reserved.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/**
 *    @file
 *      This file implements unit tests for FabricTable implementation.
 */

#include <errno.h>
#include <stdarg.h>

#include <pw_unit_test/framework.h>

#include <lib/core/CHIPCore.h>
#include <lib/core/StringBuilderAdapters.h>

#include <credentials/FabricTable.h>

#include <credentials/PersistentStorageOpCertStore.h>
#include <credentials/TestOnlyLocalCertificateAuthority.h>
#include <credentials/tests/CHIPCert_test_vectors.h>
#include <credentials/tests/CHIPCert_unit_test_vectors.h>
#include <crypto/CHIPCryptoPAL.h>
#include <crypto/PersistentStorageOperationalKeystore.h>
#include <lib/asn1/ASN1.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/TestPersistentStorageDelegate.h>
#include <lib/support/tests/ExtraPwTestMacros.h>

#include <platform/ConfigurationManager.h>

#include <lib/support/BytesToHex.h>

using namespace chip;
using namespace chip::Credentials;

namespace {

class ScopedFabricTable
{
public:
    ScopedFabricTable() {}
    ~ScopedFabricTable()
    {
        mFabricTable.Shutdown();
        mOpCertStore.Finish();
        mOpKeyStore.Finish();
    }

    CHIP_ERROR Init(chip::TestPersistentStorageDelegate * storage)
    {
        chip::FabricTable::InitParams initParams;
        initParams.storage             = storage;
        initParams.operationalKeystore = &mOpKeyStore;
        initParams.opCertStore         = &mOpCertStore;

        ReturnErrorOnFailure(mOpKeyStore.Init(storage));
        ReturnErrorOnFailure(mOpCertStore.Init(storage));
        return mFabricTable.Init(initParams);
    }

    CHIP_ERROR ReinitFabricTable(chip::TestPersistentStorageDelegate * storage)
    {
        chip::FabricTable::InitParams initParams;
        initParams.storage             = storage;
        initParams.operationalKeystore = &mOpKeyStore;
        initParams.opCertStore         = &mOpCertStore;

        return mFabricTable.Init(initParams);
    }

    FabricTable & GetFabricTable() { return mFabricTable; }

    Credentials::PersistentStorageOpCertStore & GetOpCertStore() { return mOpCertStore; }

private:
    FabricTable mFabricTable;
    PersistentStorageOperationalKeystore mOpKeyStore;
    Credentials::PersistentStorageOpCertStore mOpCertStore;
};

/**
 * Load a single test fabric with with the Root01:ICA01:Node01_01 identity.
 */
static CHIP_ERROR LoadTestFabric_Node01_01(FabricTable & fabricTable, bool doCommit)
{
    Crypto::P256SerializedKeypair opKeysSerialized;
    static Crypto::P256Keypair opKey_Node01_01;

    FabricIndex fabricIndex;
    memcpy(opKeysSerialized.Bytes(), TestCerts::sTestCert_Node01_01_PublicKey.data(),
           TestCerts::sTestCert_Node01_01_PublicKey.size());
    memcpy(opKeysSerialized.Bytes() + TestCerts::sTestCert_Node01_01_PublicKey.size(),
           TestCerts::sTestCert_Node01_01_PrivateKey.data(), TestCerts::sTestCert_Node01_01_PrivateKey.size());

    ByteSpan rcacSpan(TestCerts::sTestCert_Root01_Chip);
    ByteSpan icacSpan(TestCerts::sTestCert_ICA01_Chip);
    ByteSpan nocSpan(TestCerts::sTestCert_Node01_01_Chip);

    ReturnErrorOnFailure(opKeysSerialized.SetLength(TestCerts::sTestCert_Node01_01_PublicKey.size() +
                                                    TestCerts::sTestCert_Node01_01_PrivateKey.size()));
    ReturnErrorOnFailure(opKey_Node01_01.Deserialize(opKeysSerialized));
    ReturnErrorOnFailure(fabricTable.AddNewPendingTrustedRootCert(rcacSpan));

    ReturnErrorOnFailure(fabricTable.AddNewPendingFabricWithProvidedOpKey(nocSpan, icacSpan, VendorId::TestVendor1,
                                                                          &opKey_Node01_01,
                                                                          /*isExistingOpKeyExternallyOwned =*/true, &fabricIndex));
    if (doCommit)
    {
        ReturnErrorOnFailure(fabricTable.CommitPendingFabricData());
    }

    return CHIP_NO_ERROR;
}

static CHIP_ERROR LoadTestFabric_Node01_02(FabricTable & fabricTable, bool doCommit)
{
    Crypto::P256SerializedKeypair opKeysSerialized;
    FabricIndex fabricIndex;
    static Crypto::P256Keypair opKey_Node01_02;

    memcpy(opKeysSerialized.Bytes(), TestCerts::sTestCert_Node01_02_PublicKey.data(),
           TestCerts::sTestCert_Node01_02_PublicKey.size());
    memcpy(opKeysSerialized.Bytes() + TestCerts::sTestCert_Node01_02_PublicKey.size(),
           TestCerts::sTestCert_Node01_02_PrivateKey.data(), TestCerts::sTestCert_Node01_02_PrivateKey.size());

    ByteSpan rcacSpan(TestCerts::sTestCert_Root01_Chip);
    ByteSpan nocSpan(TestCerts::sTestCert_Node01_02_Chip);

    ReturnErrorOnFailure(opKeysSerialized.SetLength(TestCerts::sTestCert_Node01_02_PublicKey.size() +
                                                    TestCerts::sTestCert_Node01_02_PrivateKey.size()));
    ReturnErrorOnFailure(opKey_Node01_02.Deserialize(opKeysSerialized));

    ReturnErrorOnFailure(fabricTable.AddNewPendingTrustedRootCert(rcacSpan));

    ReturnErrorOnFailure(fabricTable.AddNewPendingFabricWithProvidedOpKey(nocSpan, {}, VendorId::TestVendor1, &opKey_Node01_02,
                                                                          /*isExistingOpKeyExternallyOwned =*/true, &fabricIndex));
    if (doCommit)
    {
        ReturnErrorOnFailure(fabricTable.CommitPendingFabricData());
    }

    return CHIP_NO_ERROR;
}

/**
 * Load a single test fabric with with the Root02:ICA02:Node02_01 identity.
 */
static CHIP_ERROR LoadTestFabric_Node02_01(FabricTable & fabricTable, bool doCommit,
                                           FabricTable::AdvertiseIdentity advertiseIdentity = FabricTable::AdvertiseIdentity::Yes)
{
    Crypto::P256SerializedKeypair opKeysSerialized;
    FabricIndex fabricIndex;
    static Crypto::P256Keypair opKey_Node02_01;

    memcpy(opKeysSerialized.Bytes(), TestCerts::sTestCert_Node02_01_PublicKey.data(),
           TestCerts::sTestCert_Node02_01_PublicKey.size());
    memcpy(opKeysSerialized.Bytes() + TestCerts::sTestCert_Node02_01_PublicKey.size(),
           TestCerts::sTestCert_Node02_01_PrivateKey.data(), TestCerts::sTestCert_Node02_01_PrivateKey.size());

    ByteSpan rcacSpan(TestCerts::sTestCert_Root02_Chip);
    ByteSpan icacSpan(TestCerts::sTestCert_ICA02_Chip);
    ByteSpan nocSpan(TestCerts::sTestCert_Node02_01_Chip);

    EXPECT_EQ(opKeysSerialized.SetLength(TestCerts::sTestCert_Node02_01_PublicKey.size() +
                                         TestCerts::sTestCert_Node02_01_PrivateKey.size()),
              CHIP_NO_ERROR);
    EXPECT_EQ(opKey_Node02_01.Deserialize(opKeysSerialized), CHIP_NO_ERROR);

    EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcacSpan), CHIP_NO_ERROR);

    CHIP_ERROR err =
        fabricTable.AddNewPendingFabricWithProvidedOpKey(nocSpan, icacSpan, VendorId::TestVendor1, &opKey_Node02_01,
                                                         /*isExistingOpKeyExternallyOwned =*/true, &fabricIndex, advertiseIdentity);
    EXPECT_EQ(err, CHIP_NO_ERROR);

    if (doCommit)
    {
        err = fabricTable.CommitPendingFabricData();
        EXPECT_EQ(err, CHIP_NO_ERROR);
    }

    return err;
}

const FabricInfo * FindFabric(FabricTable & fabricTable, ByteSpan rootPublicKey, FabricId fabricId)
{
    Crypto::P256PublicKey key;
    EXPECT_GE(key.Length(), rootPublicKey.size());
    if (key.Length() < rootPublicKey.size())
    {
        return nullptr;
    }
    memcpy(key.Bytes(), rootPublicKey.data(), rootPublicKey.size());
    return fabricTable.FindFabric(key, fabricId);
}

CHIP_ERROR VerifySignatureWithNocPublicKey(FabricTable & fabricTable, FabricIndex fabricIndex, ByteSpan message,
                                           ByteSpan signatureBytes)
{
    uint8_t nocCertBuf[kMaxCHIPCertLength];
    MutableByteSpan nocCertSpan{ nocCertBuf };

    ReturnErrorOnFailure(fabricTable.FetchNOCCert(fabricIndex, nocCertSpan));

    P256PublicKeySpan nocPublicKeySpan;
    ReturnErrorOnFailure(ExtractPublicKeyFromChipCert(nocCertSpan, nocPublicKeySpan));

    Crypto::P256PublicKey nocPublicKey(nocPublicKeySpan);

    VerifyOrReturnError(signatureBytes.size() >= Crypto::P256ECDSASignature::Capacity(), CHIP_ERROR_BUFFER_TOO_SMALL);
    Crypto::P256ECDSASignature signature;
    memcpy(signature.Bytes(), signatureBytes.data(), signature.Capacity());
    EXPECT_SUCCESS(signature.SetLength(signature.Capacity()));

    return nocPublicKey.ECDSA_validate_msg_signature(message.data(), message.size(), signature);
}

struct TestFabricTable : public ::testing::Test
{

    static void SetUpTestSuite()
    {
        DeviceLayer::SetConfigurationMgr(&DeviceLayer::ConfigurationManagerImpl::GetDefaultInstance());
        ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR);
#if CHIP_CRYPTO_PSA
        ASSERT_EQ(psa_crypto_init(), PSA_SUCCESS);
#endif
    }
    static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); }

    FabricIndex CreateBasicFabricForVidVerification(FabricTable & fabricTable, Crypto::P256SerializedKeypair & rootKeyForTest,
                                                    FabricId fabricId, uint16_t vendorId)
    {
        constexpr NodeId kNodeId = 1;

        Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;
        VerifyOrDie(fabricCertAuthority.Init(rootKeyForTest).IsSuccess());

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        SuccessOrDie(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan));

        VerifyOrDie(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, kNodeId, csrSpan).GetStatus() ==
                    CHIP_NO_ERROR);
        ByteSpan rcac = fabricCertAuthority.GetRcac();
        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        SuccessOrDie(fabricTable.AddNewPendingTrustedRootCert(rcac));
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        SuccessOrDie(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, vendorId, &newFabricIndex));
        SuccessOrDie(fabricTable.CommitPendingFabricData());

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(newFabricIndex);
        VerifyOrDie(fabricInfo != nullptr);
        VerifyOrDie(fabricInfo->GetFabricId() == fabricId);
        VerifyOrDie(fabricInfo->GetVendorId() == vendorId);

        return fabricInfo->GetFabricIndex();
    }
};

TEST_F(TestFabricTable, TestLastKnownGoodTimeInit)
{
    // Fabric table init should init Last Known Good Time to the firmware build time.
    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;

    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    System::Clock::Seconds32 lastKnownGoodChipEpochTime;

    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
    EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodChipEpochTime), CHIP_NO_ERROR);
    System::Clock::Seconds32 firmwareBuildTime;
    EXPECT_EQ(DeviceLayer::ConfigurationMgr().GetFirmwareBuildChipEpochTime(firmwareBuildTime), CHIP_NO_ERROR);
    EXPECT_EQ(lastKnownGoodChipEpochTime, firmwareBuildTime);
}

TEST_F(TestFabricTable, TestCollidingFabrics)
{
    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    //
    // Start by loading NOCs for two nodes on the same fabric. The second one should fail since the FabricTable by default
    // doesn't permit colliding fabrics.
    //
    EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);
    EXPECT_NE(LoadTestFabric_Node01_02(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

    //
    // Revert the partially added NOC from the last call, permit colliding fabrics in the FabricTable and try again.
    // This time, it should succeed
    //
    fabricTable.RevertPendingFabricData();
    fabricTable.PermitCollidingFabrics();
    EXPECT_EQ(LoadTestFabric_Node01_02(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

    ByteSpan rcacSpan(TestCerts::sTestCert_Root01_Chip);
    Credentials::P256PublicKeySpan rootPublicKeySpan;

    EXPECT_EQ(Credentials::ExtractPublicKeyFromChipCert(rcacSpan, rootPublicKeySpan), CHIP_NO_ERROR);

    //
    // Ensure we can find both node identities in the FabricTable.
    //
    {
        chip::Platform::ScopedMemoryBuffer<uint8_t> nocBuf;
        ByteSpan origNocSpan(TestCerts::sTestCert_Node01_01_Chip);
        NodeId nodeId;
        FabricId fabricId;

        EXPECT_EQ(ExtractNodeIdFabricIdFromOpCert(origNocSpan, &nodeId, &fabricId), CHIP_NO_ERROR);
        EXPECT_NE(fabricTable.FindIdentity(rootPublicKeySpan, fabricId, nodeId), nullptr);
    }

    {
        chip::Platform::ScopedMemoryBuffer<uint8_t> nocBuf;
        ByteSpan origNocSpan(TestCerts::sTestCert_Node01_02_Chip);
        NodeId nodeId;
        FabricId fabricId;

        EXPECT_EQ(ExtractNodeIdFabricIdFromOpCert(origNocSpan, &nodeId, &fabricId), CHIP_NO_ERROR);
        EXPECT_NE(fabricTable.FindIdentity(rootPublicKeySpan, fabricId, nodeId), nullptr);
    }
}

TEST_F(TestFabricTable, TestUpdateLastKnownGoodTime)
{
    // Adding a fabric should advance Last Known Good Time if any certificate's
    // NotBefore time is later than the build time, and else should leave it
    // to the initial build time value.

    // Test certs all have this NotBefore: Oct 15 14:23:43 2020 GMT
    const ASN1::ASN1UniversalTime asn1Expected = { 2020, 10, 15, 14, 23, 43 };
    uint32_t testCertNotBeforeSeconds;
    EXPECT_EQ(Credentials::ASN1ToChipEpochTime(asn1Expected, testCertNotBeforeSeconds), CHIP_NO_ERROR);
    System::Clock::Seconds32 testCertNotBeforeTime = System::Clock::Seconds32(testCertNotBeforeSeconds);

    // Test that certificate NotBefore times that are before the Firmware build time
    // do not advance Last Known Good Time.
    System::Clock::Seconds32 afterNotBeforeBuildTimes[] = { System::Clock::Seconds32(testCertNotBeforeTime.count() + 1),
                                                            System::Clock::Seconds32(testCertNotBeforeTime.count() + 1000),
                                                            System::Clock::Seconds32(testCertNotBeforeTime.count() + 1000000) };
    for (auto buildTime : afterNotBeforeBuildTimes)
    {
        // Set build time to the desired value.
        EXPECT_EQ(DeviceLayer::ConfigurationMgr().SetFirmwareBuildChipEpochTime(buildTime), CHIP_NO_ERROR);
        chip::TestPersistentStorageDelegate testStorage;

        {
            // Initialize a fabric table.
            ScopedFabricTable fabricTableHolder;
            EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
            FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

            // Read back Last Known Good Time, which will have been initialized to firmware build time.
            System::Clock::Seconds32 lastKnownGoodTime;
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, buildTime);

            // Load a test fabric, but do not commit.
            EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ false), CHIP_NO_ERROR);

            // Read Last Known Good Time and verify that it hasn't moved forward.
            // This test case was written after the test certs' NotBefore time and we
            // are using a configuration manager that should reflect a real build time.
            // Therefore, we expect that build time is after NotBefore and so Last
            // Known Good Time will be set to the later of these, build time, even
            // after installing the new fabric.
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, buildTime);

            // Verify that calling the fail-safe roll back interface does not change
            // last known good time, as it hadn't been updated in the first place.
            fabricTable.RevertPendingFabricData();
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, buildTime);

            // Now reload the test fabric and commit this time.
            EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

            // Read Last Known Good Time and verify that it hasn't moved forward.
            // This test case was written after the test certs' NotBefore time and we
            // are using a configuration manager that should reflect a real build time.
            // Therefore, we expect that build time is after NotBefore and so Last
            // Known Good Time will be set to the later of these, build time, even
            // after installing the new fabric.
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, buildTime);

            // Call revert again.  Since we've committed, this is a no-op.
            // Last known good time should again be unchanged.
            fabricTable.RevertPendingFabricData();
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, buildTime);
        }
        {
            // Test reloading last known good time from persistence.
            ScopedFabricTable fabricTableHolder;
            EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
            FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

            // Verify that last known good time was retained.
            System::Clock::Seconds32 lastKnownGoodTime;
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, buildTime);
        }
    }

    System::Clock::Seconds32 beforeNotBeforeBuildTimes[] = { testCertNotBeforeTime,
                                                             System::Clock::Seconds32(testCertNotBeforeTime.count() - 1),
                                                             System::Clock::Seconds32(testCertNotBeforeTime.count() - 1000),
                                                             System::Clock::Seconds32(testCertNotBeforeTime.count() - 1000000) };
    // Test that certificate NotBefore times that are at or after the Firmware
    // build time do result in Last Known Good Times set to these.
    // Verify behavior both for fail-safe roll back and commit scenarios.
    for (auto buildTime : beforeNotBeforeBuildTimes)
    {
        // Set build time to the desired value.
        EXPECT_EQ(DeviceLayer::ConfigurationMgr().SetFirmwareBuildChipEpochTime(buildTime), CHIP_NO_ERROR);
        chip::TestPersistentStorageDelegate testStorage;
        {
            // Initialize a fabric table.
            ScopedFabricTable fabricTableHolder;
            EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
            FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

            // Load a test fabric, but do not commit.
            EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ false), CHIP_NO_ERROR);

            // Read Last Known Good Time and verify that it is now set to the certificate
            // NotBefore time, as this should be at or after firmware build time.
            System::Clock::Seconds32 lastKnownGoodTime;
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, testCertNotBeforeTime);

            // Now test revert.  Last known good time should change back to the
            // previous value.
            fabricTable.RevertPendingFabricData();
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, buildTime);
        }
        {
            // Test reloading last known good time from persistence.
            ScopedFabricTable fabricTableHolder;
            EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
            FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

            // Verify that the original last known good time was retained, since
            // we reverted before.
            System::Clock::Seconds32 lastKnownGoodTime;
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
        }
        {
            // Now test loading a fabric and committing.
            ScopedFabricTable fabricTableHolder;
            EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
            FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
            EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

            // Read Last Known Good Time and verify that it is now set to the certificate
            // NotBefore time, as this should be at or after firmware build time.
            System::Clock::Seconds32 lastKnownGoodTime;
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, testCertNotBeforeTime);

            // Now test revert, which will be a no-op because we already
            // committed.  Verify that Last Known Good time is retained.
            fabricTable.RevertPendingFabricData();
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, testCertNotBeforeTime);
        }
        {
            // Test reloading last known good time from persistence.
            ScopedFabricTable fabricTableHolder;
            EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
            FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

            // Verify that the new last known good time was retained, since
            // we committed.
            System::Clock::Seconds32 lastKnownGoodTime;
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, testCertNotBeforeTime);
        }
    }
}

TEST_F(TestFabricTable, TestSetLastKnownGoodTime)
{
    // It is desirable for nodes to set Last Known Good Time whenever a good
    // time source is available, including cases where this would set the time
    // backward.  However, it is impermissible to set last known good time to
    // any time before the Firmware Build time or the latest NotBefore of any
    // installed certificate.

    // Test certs all have this NotBefore: Oct 15 14:23:43 2020 GMT
    const ASN1::ASN1UniversalTime asn1Expected = { 2020, 10, 15, 14, 23, 43 };
    uint32_t testCertNotBeforeSeconds;
    EXPECT_EQ(Credentials::ASN1ToChipEpochTime(asn1Expected, testCertNotBeforeSeconds), CHIP_NO_ERROR);
    System::Clock::Seconds32 testCertNotBeforeTime = System::Clock::Seconds32(testCertNotBeforeSeconds);

    // Iterate over two cases: one with build time prior to our certificates' NotBefore, one with build time after.
    System::Clock::Seconds32 testCaseFirmwareBuildTimes[] = { System::Clock::Seconds32(testCertNotBeforeTime.count() - 100000),
                                                              System::Clock::Seconds32(testCertNotBeforeTime.count() + 100000) };

    for (auto buildTime : testCaseFirmwareBuildTimes)
    {
        // Set build time to the desired value.
        EXPECT_EQ(DeviceLayer::ConfigurationMgr().SetFirmwareBuildChipEpochTime(buildTime), CHIP_NO_ERROR);
        chip::TestPersistentStorageDelegate testStorage;
        System::Clock::Seconds32 newTime;
        {
            // Initialize a fabric table.
            ScopedFabricTable fabricTableHolder;
            EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
            FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

            // Load a test fabric
            EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit= */ true), CHIP_NO_ERROR);

            // Verify the Last Known Good Time matches our expected initial value.
            System::Clock::Seconds32 initialLastKnownGoodTime =
                buildTime > testCertNotBeforeTime ? buildTime : testCertNotBeforeTime;
            System::Clock::Seconds32 lastKnownGoodTime;
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime);

            // Read Last Known Good Time and verify that it hasn't moved forward, since
            // build time is later than the test certs' NotBefore times.
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime);

            // Attempt to set a Last Known Good Time that is before the firmware build time.  This should fail.
            newTime = System::Clock::Seconds32(buildTime.count() - 1000);
            EXPECT_NE(fabricTable.SetLastKnownGoodChipEpochTime(newTime), CHIP_NO_ERROR);

            // Verify Last Known Good Time is unchanged.
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime);

            // Attempt to set a Last Known Good Time that is before our certificates' NotBefore times.  This should fail.
            newTime = System::Clock::Seconds32(testCertNotBeforeTime.count() - 1000);
            EXPECT_NE(fabricTable.SetLastKnownGoodChipEpochTime(newTime), CHIP_NO_ERROR);

            // Verify Last Known Good Time is unchanged.
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime);

            // Attempt to set a Last Known Good Time that at our current value.
            EXPECT_EQ(fabricTable.SetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);

            // Verify Last Known Good Time is unchanged.
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, initialLastKnownGoodTime);

            // Attempt to set Last Known Good Times that is after our current value.
            newTime = System::Clock::Seconds32(initialLastKnownGoodTime.count() + 1000);
            EXPECT_EQ(fabricTable.SetLastKnownGoodChipEpochTime(newTime), CHIP_NO_ERROR);

            // Verify Last Known Good Time is updated.
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, newTime);
        }
        {
            // Verify that Last Known Good Time was persisted.

            ScopedFabricTable fabricTableHolder;
            EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
            FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

            System::Clock::Seconds32 lastKnownGoodTime;
            EXPECT_EQ(fabricTable.GetLastKnownGoodChipEpochTime(lastKnownGoodTime), CHIP_NO_ERROR);
            EXPECT_EQ(lastKnownGoodTime, newTime);
        }
    }
}

// Test adding 2 fabrics, updating 1, removing 1
TEST_F(TestFabricTable, TestBasicAddNocUpdateNocFlow)
{
    Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority;
    Credentials::TestOnlyLocalCertificateAuthority fabric44CertAuthority;

    chip::TestPersistentStorageDelegate storage;

    // Uncomment the next line for superior debugging powers if you blow-up this test
    // storage.SetLoggingLevel(chip::TestPersistentStorageDelegate::LoggingLevel::kLogMutation);

    // Initialize test CA and a Fabric 11 externally owned key
    EXPECT_TRUE(fabric11CertAuthority.Init().IsSuccess());
    EXPECT_TRUE(fabric44CertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    chip::Crypto::P256Keypair fabric11Node55Keypair; // Fabric ID 11,
    EXPECT_EQ(fabric11Node55Keypair.Initialize(Crypto::ECPKeyTarget::ECDSA), CHIP_NO_ERROR);

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);
    EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);

    {
        FabricIndex nextFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(nextFabricIndex, 1);
    }

    size_t numFabricsIterated = 0;

    size_t numStorageKeysAtStart = storage.GetNumKeys();

    // Sequence 1: Add node ID 55 on fabric 11, using externally owned key and no ICAC --> Yield fabricIndex 1
    {
        FabricId fabricId = 11;
        NodeId nodeId     = 55;
        EXPECT_EQ(fabric11CertAuthority.SetIncludeIcac(false)
                      .GenerateNocChain(fabricId, nodeId, fabric11Node55Keypair.Pubkey())
                      .GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric11CertAuthority.GetRcac();
        ByteSpan noc  = fabric11CertAuthority.GetNoc();

        // Validate iterator sees nothing yet
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 0u);
            EXPECT_FALSE(saw1);
        }

        uint8_t rcacBuf[Credentials::kMaxCHIPCertLength];
        {
            // No pending root cert yet.
            MutableByteSpan fetchedSpan{ rcacBuf };
            EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_ERROR_NOT_FOUND);
        }

        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        {
            // Now have a pending root cert.
            MutableByteSpan fetchedSpan{ rcacBuf };
            EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_NO_ERROR);
            EXPECT_TRUE(fetchedSpan.data_equal(rcac));
        }
        EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);

        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        bool keyIsExternallyOwned  = true;

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithProvidedOpKey(noc, ByteSpan{}, kVendorId, &fabric11Node55Keypair,
                                                                   keyIsExternallyOwned, &newFabricIndex),
                  CHIP_NO_ERROR);
        EXPECT_EQ(newFabricIndex, 1);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        // After adding the pending new fabric (equivalent of AddNOC processing), the new
        // fabric must be pending.
        EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), 1);

        {
            // No more pending root cert; it's associated with a fabric now.
            MutableByteSpan fetchedSpan{ rcacBuf };
            EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_ERROR_NOT_FOUND);
        }

        // No storage yet
        EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAtStart);

        // Next fabric index has not been updated yet.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 1);
        }

        // Validate iterator sees pending
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), nodeId);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), fabricId);
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }

        // Commit, now storage should have keys
        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        EXPECT_EQ(storage.GetNumKeys(), (numStorageKeysAtStart + 4)); // 2 opcerts + fabric metadata + index

        // Next fabric index has been updated.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 2);
        }

        // Fabric can't be pending anymore.
        EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), newFabricIndex);
        EXPECT_EQ(fabricInfo->GetNodeId(), nodeId);
        EXPECT_EQ(fabricInfo->GetFabricId(), fabricId);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

        // Validate that fabric has the correct operational key by verifying a signature
        Crypto::P256ECDSASignature sig;
        uint8_t message[] = { 'm', 's', 'g' };
        EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
        EXPECT_EQ(fabric11Node55Keypair.Pubkey().ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);

        // Validate iterator sees committed
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), nodeId);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), fabricId);
                    EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }
    }

    size_t numStorageAfterFirstAdd = storage.GetNumKeys();

    // Sequence 2: Add VVS and VVSC (VVSC possible since fabric 11 doesn't have ICAC). Make sure additional storage is present.
    {
        constexpr FabricIndex kFirstFabricIndex = 1u;

        // VVSC contents is not checked, so can be just zero bytes.
        uint8_t vvsc[kMaxCHIPCertLength];
        memset(&vvsc[0], 0x00, sizeof(vvsc));
        ByteSpan vvscSpan{ vvsc };

        // VVS contents is not checked, except first by that must be 0x01.
        uint8_t vvs[Crypto::kVendorIdVerificationStatementV1Size];
        memset(&vvs[0], 0x01, sizeof(vvs));
        ByteSpan vvsSpan{ vvs };

        // Set new VVS, VVSC. Applies immediately due to no intermediate NOC update flow present.
        bool fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(kFirstFabricIndex, NullOptional, MakeOptional(vvsSpan),
                                                                  MakeOptional(vvscSpan), fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, true);

        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterFirstAdd + 2); // VVSC and VVS added.

        // Make sure VVSC was stored.
        {
            uint8_t readBackVvscBuf[kMaxCHIPCertLength];
            memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
            MutableByteSpan readBackVvscSpan{ readBackVvscBuf };

            EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                          kFirstFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
                      CHIP_NO_ERROR);
            EXPECT_TRUE(readBackVvscSpan.data_equal(vvscSpan));
        }

        // Make sure VVS was also set pending
        {
            uint8_t readBackVvsBuf[Crypto::kVendorIdVerificationStatementV1Size];
            memset(&readBackVvsBuf[0], 0x00, sizeof(readBackVvsBuf));
            MutableByteSpan readBackVvsSpan{ readBackVvsBuf };

            EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                          kFirstFabricIndex, OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement,
                          readBackVvsSpan),
                      CHIP_NO_ERROR);
            EXPECT_TRUE(readBackVvsSpan.data_equal(vvsSpan));
        }
    }

    numStorageAfterFirstAdd = storage.GetNumKeys(); // Accounts now for VVSC + VVS

    // Sequence 3: Add node ID 999 on fabric 44, using operational keystore and ICAC --> Yield fabricIndex 2
    {
        FabricId fabricId = 44;
        NodeId nodeId     = 999;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric44CertAuthority.GetRcac();
        ByteSpan icac = fabric44CertAuthority.GetIcac();
        ByteSpan noc  = fabric44CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 1);

        // Next fabric index should still be the same as before.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 2);
        }

        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);

        FabricIndex newFabricIndex = kUndefinedFabricIndex;

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 2);
        EXPECT_EQ(newFabricIndex, 2);
        EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), 2);

        // No storage yet
        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterFirstAdd);
        // Next fabric index has not been updated yet.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 2);
        }

        // Commit, now storage should have keys
        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 2);

        EXPECT_EQ(storage.GetNumKeys(),
                  (numStorageAfterFirstAdd + 5)); // 3 opcerts + fabric metadata + 1 operational key

        // Next fabric index has been updated.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 3);
        }

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
        ASSERT_NE(fabricInfo, nullptr);
        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), newFabricIndex);
        EXPECT_EQ(fabricInfo->GetNodeId(), nodeId);
        EXPECT_EQ(fabricInfo->GetFabricId(), fabricId);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

        // Validate that fabric has the correct operational key by verifying a signature
        {
            Crypto::P256ECDSASignature sig;
            uint8_t message[] = { 'm', 's', 'g' };

            Crypto::P256PublicKey nocPubKey;
            EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR);

            EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
            EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
        }

        // Verify we can now see 2 fabrics with the iterator
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            bool saw2          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 11u);
                    EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
                    saw1 = true;
                }
                if (iterFabricInfo.GetFabricIndex() == 2)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
                    saw2 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 2u);
            EXPECT_TRUE(saw1);
            EXPECT_TRUE(saw2);
        }
    }

    size_t numStorageAfterSecondAdd = storage.GetNumKeys();

    // Sequence 4: Update node ID 999 to 1000 on fabric 44, using operational keystore and no ICAC --> Stays fabricIndex 2
    {
        FabricId fabricId       = 44;
        NodeId nodeId           = 1000;
        FabricIndex fabricIndex = 2;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };

        // Make sure to tag fabric index to pending opkey: otherwise the UpdateNOC fails
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::MakeOptional(static_cast<FabricIndex>(2)), csrSpan),
                  CHIP_NO_ERROR);

        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric44CertAuthority.GetRcac();
        ByteSpan noc  = fabric44CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 2);
        EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(2, noc, ByteSpan{}, FabricTable::AdvertiseIdentity::No),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 2);

        // No storage yet
        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterSecondAdd);

        // Validate iterator sees the pending data
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            bool saw2          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 11u);
                    EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
                    saw1 = true;
                }
                if (iterFabricInfo.GetFabricIndex() == 2)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 1000u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    EXPECT_FALSE(iterFabricInfo.ShouldAdvertiseIdentity());
                    saw2 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 2u);
            EXPECT_TRUE(saw1);
            EXPECT_TRUE(saw2);
        }

        // Commit, now storage should have keys
        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 2);

        EXPECT_EQ(storage.GetNumKeys(), (numStorageAfterSecondAdd - 1)); // ICAC got deleted

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
        ASSERT_NE(fabricInfo, nullptr);
        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), fabricIndex);
        EXPECT_EQ(fabricInfo->GetNodeId(), nodeId);
        EXPECT_EQ(fabricInfo->GetFabricId(), fabricId);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(fabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

        // Validate that fabric has the correct operational key by verifying a signature
        {
            Crypto::P256ECDSASignature sig;
            uint8_t message[] = { 'm', 's', 'g' };

            Crypto::P256PublicKey nocPubKey;
            EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR);

            EXPECT_EQ(fabricTable.SignWithOpKeypair(fabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
            EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
        }

        // Validate iterator sees the committed update
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            bool saw2          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 11u);
                    saw1 = true;
                }
                if (iterFabricInfo.GetFabricIndex() == 2)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 1000u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    saw2 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 2u);
            EXPECT_TRUE(saw1);
            EXPECT_TRUE(saw2);
        }

        // Next fabric index has stayed the same.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 3);
        }
    }

    size_t numStorageAfterUpdate = storage.GetNumKeys();

    // Sequence 5: Rename fabric index 2, applies immediately when nothing pending
    {
        EXPECT_EQ(fabricTable.FabricCount(), 2);
        EXPECT_EQ(fabricTable.SetFabricLabel(2, "roboto"_span), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 2);

        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterUpdate); // Number of keys unchanged

        // Validate basic contents
        {
            const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
            ASSERT_NE(fabricInfo, nullptr);
            EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
            EXPECT_EQ(fabricInfo->GetNodeId(), 1000u);
            EXPECT_EQ(fabricInfo->GetFabricId(), 44u);
            EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
            EXPECT_TRUE(fabricInfo->GetFabricLabel().data_equal(CharSpan{ "roboto", strlen("roboto") }));
        }

        // Next fabric index has stayed the same.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 3);
        }
    }

    // Sequence 6: Remove FabricIndex 1 (FabricId 11, NodeId 55), make sure FabricIndex 2 (FabricId 44, NodeId 1000) still exists
    {
        // Remove the fabric: no commit needed
        {
            EXPECT_EQ(fabricTable.FabricCount(), 2);
            EXPECT_EQ(fabricTable.Delete(1), CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 1);

            EXPECT_EQ(storage.GetNumKeys(), (numStorageAfterUpdate - 5)); // Deleted NOC, RCAC, Metadata, VVS, VVSC
        }

        // Next fabric index has stayed the same.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 3);
        }

        // Validate contents of Fabric Index 2 is still OK.
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
        EXPECT_EQ(fabricInfo->GetNodeId(), 1000u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 44u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_TRUE(fabricInfo->GetFabricLabel().data_equal(CharSpan{ "roboto", strlen("roboto") }));

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(2, rootPublicKeyOfFabric), CHIP_NO_ERROR);

        // Validate that fabric has the correct operational key by verifying a signature
        {
            uint8_t nocBuf[Credentials::kMaxCHIPCertLength];
            MutableByteSpan nocSpan{ nocBuf };
            EXPECT_EQ(fabricTable.FetchNOCCert(2, nocSpan), CHIP_NO_ERROR);

            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(nocSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey nocPubKey(certificates.GetCertSet()[0].mPublicKey);

            Crypto::P256ECDSASignature sig;
            uint8_t message[] = { 'm', 's', 'g' };

            EXPECT_EQ(fabricTable.SignWithOpKeypair(2, ByteSpan{ message }, sig), CHIP_NO_ERROR);
            EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
        }

        // Validate iterator only sees the remaining fabric
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            bool saw2          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    saw1 = true;
                }
                if (iterFabricInfo.GetFabricIndex() == 2)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 1000u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    saw2 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_FALSE(saw1);
            EXPECT_TRUE(saw2);
        }
    }
}

TEST_F(TestFabricTable, TestAddMultipleSameRootDifferentFabricId)
{
    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;

    chip::TestPersistentStorageDelegate storage;
    EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    uint8_t rcac1Buf[kMaxCHIPCertLength];
    MutableByteSpan rcac1Span{ rcac1Buf };

    // First scope: add FabricID 1111, node ID 55
    {
        FabricId fabricId = 1111;
        NodeId nodeId     = 55;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan rcac = fabricCertAuthority.GetRcac();
        // Keep a copy for second scope check
        EXPECT_SUCCESS(CopySpanToMutableSpan(rcac, rcac1Span));

        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex,
                                                                         FabricTable::AdvertiseIdentity::No),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(newFabricIndex, 1);

        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);
        EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity());

        EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(newFabricIndex, FabricTable::AdvertiseIdentity::Yes), CHIP_NO_ERROR);
        EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());

        // Check that for indices we don't have a fabric for, SetShouldAdvertiseIdentity fails.
        EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(kUndefinedFabricIndex, FabricTable::AdvertiseIdentity::No),
                  CHIP_ERROR_INVALID_FABRIC_INDEX);
        EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(kUndefinedFabricIndex, FabricTable::AdvertiseIdentity::Yes),
                  CHIP_ERROR_INVALID_FABRIC_INDEX);
        EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(2, FabricTable::AdvertiseIdentity::Yes), CHIP_ERROR_INVALID_FABRIC_INDEX);
        EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(2, FabricTable::AdvertiseIdentity::No), CHIP_ERROR_INVALID_FABRIC_INDEX);

        EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());

        EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(newFabricIndex, FabricTable::AdvertiseIdentity::No), CHIP_NO_ERROR);
        EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity());
    }
    size_t numStorageKeysAfterFirstAdd = storage.GetNumKeys();
    EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time

    // Second scope: add FabricID 2222, node ID 66, same root as first
    {
        FabricId fabricId = 2222;
        NodeId nodeId     = 66;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan rcac2 = fabricCertAuthority.GetRcac();
        EXPECT_TRUE(rcac2.data_equal(rcac1Span));

        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac2), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 2);
        EXPECT_EQ(newFabricIndex, 2);

        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
        EXPECT_EQ(fabricInfo->GetNodeId(), 66u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 2222u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());

        EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(newFabricIndex, FabricTable::AdvertiseIdentity::No), CHIP_NO_ERROR);
        EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity());

        EXPECT_EQ(fabricTable.SetShouldAdvertiseIdentity(newFabricIndex, FabricTable::AdvertiseIdentity::Yes), CHIP_NO_ERROR);
        EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());
    }
    size_t numStorageKeysAfterSecondAdd = storage.GetNumKeys();
    EXPECT_EQ(numStorageKeysAfterSecondAdd, (numStorageKeysAfterFirstAdd + 5)); // Add 3 certs, 1 metadata, 1 opkey
}

TEST_F(TestFabricTable, TestAddMultipleSameFabricIdDifferentRoot)
{
    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority1;
    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority2;

    chip::TestPersistentStorageDelegate storage;
    EXPECT_TRUE(fabricCertAuthority1.Init().IsSuccess());
    EXPECT_TRUE(fabricCertAuthority2.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    uint8_t rcac1Buf[kMaxCHIPCertLength];
    MutableByteSpan rcac1Span{ rcac1Buf };

    // First scope: add FabricID 1111, node ID 55
    {
        FabricId fabricId = 1111;
        NodeId nodeId     = 55;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority1.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan rcac = fabricCertAuthority1.GetRcac();
        // Keep a copy for second scope check
        EXPECT_SUCCESS(CopySpanToMutableSpan(rcac, rcac1Span));

        ByteSpan icac = fabricCertAuthority1.GetIcac();
        ByteSpan noc  = fabricCertAuthority1.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(newFabricIndex, 1);

        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);
    }
    size_t numStorageKeysAfterFirstAdd = storage.GetNumKeys();
    EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time

    // Second scope: add FabricID 1111, node ID 66, different root from first
    {
        FabricId fabricId = 1111;
        NodeId nodeId     = 66;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority2.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan rcac2 = fabricCertAuthority2.GetRcac();
        EXPECT_FALSE(rcac2.data_equal(rcac1Span));

        ByteSpan icac = fabricCertAuthority2.GetIcac();
        ByteSpan noc  = fabricCertAuthority2.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac2), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 2);
        EXPECT_EQ(newFabricIndex, 2);

        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
        EXPECT_EQ(fabricInfo->GetNodeId(), 66u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);
    }
    size_t numStorageKeysAfterSecondAdd = storage.GetNumKeys();
    EXPECT_EQ(numStorageKeysAfterSecondAdd, (numStorageKeysAfterFirstAdd + 5)); // Add 3 certs, 1 metadata, 1 opkey
}

TEST_F(TestFabricTable, TestPersistence)
{
    /**
     *
     * - Create an outer scope with storage delegate
     *   - Keep buffer slots for the operational public keys of the 2 fabrics added in the next scope
     *
     * - Create a new scope with a ScopedFabricTable
     *   - Add 2 fabrics, fully committed, using OperationalKeystore-based storage (e.g. CSR for opkey)
     *   - Make sure to save public keys in other scope for next step
     *
     * - Create a new scope with a ScopedFabricTable
     *   - Validate that after init, it has 2 fabrics and the 2 fabrics match fabric indices expected,
     *     and that the fabric tables are usable, and that NOC can be extracted for each, and that
     *     its public key matches expectation, and that they match the public keys stored in outer scope
     *     and verify you can sign and verify messages with the opkey
     *
     */

    Crypto::P256PublicKey fIdx1PublicKey;
    Crypto::P256PublicKey fIdx2PublicKey;

    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;

    chip::TestPersistentStorageDelegate storage;

    EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    // First scope: add 2 fabrics with same root: (1111, 2222), commit them, keep track of public keys
    {
        // Initialize a FabricTable
        ScopedFabricTable fabricTableHolder;
        EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
        FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

        EXPECT_EQ(fabricTable.FabricCount(), 0);

        // Add Fabric 1111 Node Id 55
        {
            FabricId fabricId = 1111;
            NodeId nodeId     = 55;

            uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
            MutableByteSpan csrSpan{ csrBuf };
            EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

            EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                      CHIP_NO_ERROR);
            ByteSpan rcac = fabricCertAuthority.GetRcac();
            ByteSpan icac = fabricCertAuthority.GetIcac();
            ByteSpan noc  = fabricCertAuthority.GetNoc();

            EXPECT_EQ(fabricTable.FabricCount(), 0);
            EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
            FabricIndex newFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 1);
            EXPECT_EQ(newFabricIndex, 1);

            EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

            // Validate contents
            const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
            ASSERT_NE(fabricInfo, nullptr);
            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

            EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
            EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
            EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
            EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
            EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

            Crypto::P256PublicKey rootPublicKeyOfFabric;
            EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
            EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

            // Validate that fabric has the correct operational key by verifying a signature
            {
                Crypto::P256ECDSASignature sig;
                uint8_t message[] = { 'm', 's', 'g' };

                EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), fIdx1PublicKey), CHIP_NO_ERROR);

                EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
                EXPECT_EQ(fIdx1PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
            }
        }

        // Add Fabric 2222 Node Id 66, no ICAC
        {
            FabricId fabricId = 2222;
            NodeId nodeId     = 66;

            uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
            MutableByteSpan csrSpan{ csrBuf };
            EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

            EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                      CHIP_NO_ERROR);
            ByteSpan rcac = fabricCertAuthority.GetRcac();
            ByteSpan noc  = fabricCertAuthority.GetNoc();

            EXPECT_EQ(fabricTable.FabricCount(), 1);
            EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
            FabricIndex newFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex),
                      CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 2);
            EXPECT_EQ(newFabricIndex, 2);

            EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

            // Validate contents
            const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
            ASSERT_NE(fabricInfo, nullptr);
            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

            EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
            EXPECT_EQ(fabricInfo->GetNodeId(), 66u);
            EXPECT_EQ(fabricInfo->GetFabricId(), 2222u);
            EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
            EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

            Crypto::P256PublicKey rootPublicKeyOfFabric;
            EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
            EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

            // Validate that fabric has the correct operational key by verifying a signature
            {
                Crypto::P256ECDSASignature sig;
                uint8_t message[] = { 'm', 's', 'g' };

                EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), fIdx2PublicKey), CHIP_NO_ERROR);

                EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
                EXPECT_EQ(fIdx2PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
            }
        }

        EXPECT_EQ(fabricTable.FabricCount(), 2);

        // Verify we can now see 2 fabrics with the iterator
        {
            size_t numFabricsIterated = 0;
            bool saw1                 = false;
            bool saw2                 = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 1111u);
                    saw1 = true;
                }
                if (iterFabricInfo.GetFabricIndex() == 2)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 66u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 2222u);
                    saw2 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 2u);
            EXPECT_TRUE(saw1);
            EXPECT_TRUE(saw2);
        }

        // Next fabric index should now be 3, since we added 1 and 2 above.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 3);
        }
    }

    // Global: Last known good time + fabric index = 2
    // Fabric 1111: Metadata, 1 opkey, RCAC/ICAC/NOC = 5
    // Fabric 2222: Metadata, 1 opkey, RCAC/NOC = 4
    EXPECT_EQ(storage.GetNumKeys(), (2u + 5u + 4u));

    // Second scope: Validate that a fresh FabricTable loads the previously committed fabrics on Init.
    {
        // Initialize a FabricTable
        ScopedFabricTable fabricTableHolder;
        EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
        FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

        EXPECT_EQ(fabricTable.FabricCount(), 2);

        // Verify we can see 2 fabrics with the iterator
        {
            size_t numFabricsIterated = 0;
            bool saw1                 = false;
            bool saw2                 = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 1111u);
                    saw1 = true;
                }
                if (iterFabricInfo.GetFabricIndex() == 2)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 66u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 2222u);
                    saw2 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 2u);
            EXPECT_TRUE(saw1);
            EXPECT_TRUE(saw2);
        }

        // Validate contents of Fabric 2222
        {
            uint8_t rcacBuf[Credentials::kMaxCHIPCertLength];
            MutableByteSpan rcacSpan{ rcacBuf };
            EXPECT_EQ(fabricTable.FetchRootCert(2, rcacSpan), CHIP_NO_ERROR);

            const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
            ASSERT_NE(fabricInfo, nullptr);
            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(rcacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

            EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
            EXPECT_EQ(fabricInfo->GetNodeId(), 66u);
            EXPECT_EQ(fabricInfo->GetFabricId(), 2222u);
            EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
            EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

            Crypto::P256PublicKey rootPublicKeyOfFabric;
            EXPECT_EQ(fabricTable.FetchRootPubkey(2, rootPublicKeyOfFabric), CHIP_NO_ERROR);
            EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

            // Validate that fabric has the correct operational key by verifying a signature
            {
                Crypto::P256ECDSASignature sig;
                uint8_t message[] = { 'm', 's', 'g' };

                EXPECT_EQ(fabricTable.SignWithOpKeypair(2, ByteSpan{ message }, sig), CHIP_NO_ERROR);
                EXPECT_EQ(fIdx2PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
            }
        }

        // Validate contents of Fabric 1111
        {
            uint8_t rcacBuf[Credentials::kMaxCHIPCertLength];
            MutableByteSpan rcacSpan{ rcacBuf };
            EXPECT_EQ(fabricTable.FetchRootCert(1, rcacSpan), CHIP_NO_ERROR);

            const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
            ASSERT_NE(fabricInfo, nullptr);
            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(rcacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

            EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
            EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
            EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
            EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
            EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

            Crypto::P256PublicKey rootPublicKeyOfFabric;
            EXPECT_EQ(fabricTable.FetchRootPubkey(1, rootPublicKeyOfFabric), CHIP_NO_ERROR);
            EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

            // Validate that fabric has the correct operational key by verifying a signature
            {
                Crypto::P256ECDSASignature sig;
                uint8_t message[] = { 'm', 's', 'g' };

                EXPECT_EQ(fabricTable.SignWithOpKeypair(1, ByteSpan{ message }, sig), CHIP_NO_ERROR);
                EXPECT_EQ(fIdx1PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
            }

            // Validate that signing with Fabric index 2 fails to verify with fabric index 1
            {
                Crypto::P256ECDSASignature sig;
                uint8_t message[] = { 'm', 's', 'g' };

                EXPECT_EQ(fabricTable.SignWithOpKeypair(2, ByteSpan{ message }, sig), CHIP_NO_ERROR);
                EXPECT_EQ(fIdx1PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig),
                          CHIP_ERROR_INVALID_SIGNATURE);
            }
        }

        // Validate that next fabric index is still 3;
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 3);
        }
    }
}

TEST_F(TestFabricTable, TestAddNocFailSafe)
{
    Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority;
    Credentials::TestOnlyLocalCertificateAuthority fabric44CertAuthority;

    chip::TestPersistentStorageDelegate storage;

    EXPECT_TRUE(fabric11CertAuthority.Init().IsSuccess());
    EXPECT_TRUE(fabric44CertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    size_t numFabricsIterated = 0;

    size_t numStorageKeysAtStart = storage.GetNumKeys();

    // Sequence 1: Add node ID 55 on fabric 11, see that pending works, and that revert works
    {
        FabricId fabricId = 11;
        NodeId nodeId     = 55;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabric11CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric11CertAuthority.GetRcac();
        ByteSpan noc  = fabric11CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;

        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 1);
        }

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex),
                  CHIP_NO_ERROR);
        EXPECT_EQ(newFabricIndex, 1);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        // No storage yet
        EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAtStart); // Nothing yet

        // Validate iterator sees pending
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), nodeId);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), fabricId);
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }

        // Revert, should see nothing yet
        fabricTable.RevertPendingFabricData();

        EXPECT_EQ(fabricTable.FabricCount(), 0);

        // No started except fabric index metadata
        EXPECT_EQ(storage.GetNumKeys(), (numStorageKeysAtStart + 1));

        // Validate iterator sees nothing
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 0u);
            EXPECT_FALSE(saw1);
        }

        // Validate next fabric index has not changed.
        {
            FabricIndex nextFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.PeekFabricIndexForNextAddition(nextFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(nextFabricIndex, 1);
        }
    }

    size_t numStorageAfterRevert = storage.GetNumKeys();

    // Sequence 2: Add node ID 999 on fabric 44, using operational keystore and ICAC --> Yield fabricIndex 1
    {
        FabricId fabricId = 44;
        NodeId nodeId     = 999;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric44CertAuthority.GetRcac();
        ByteSpan icac = fabric44CertAuthority.GetIcac();
        ByteSpan noc  = fabric44CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(newFabricIndex, 1);
        // No storage yet
        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterRevert);

        // Commit, now storage should have keys
        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        EXPECT_EQ(storage.GetNumKeys(),
                  (numStorageAfterRevert + 5)); // 3 opcerts + fabric metadata + 1 operational key

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), newFabricIndex);
        EXPECT_EQ(fabricInfo->GetNodeId(), nodeId);
        EXPECT_EQ(fabricInfo->GetFabricId(), fabricId);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

        // Validate that fabric has the correct operational key by verifying a signature
        {
            Crypto::P256ECDSASignature sig;
            uint8_t message[] = { 'm', 's', 'g' };

            Crypto::P256PublicKey nocPubKey;
            EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR);

            EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
            EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
        }

        // Verify we can now see the fabric with the iterator
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }
    }

    size_t numStorageAfterAdd = storage.GetNumKeys();

    // Sequence 3: Do a RevertPendingFabricData() again, see that it doesn't affect existing fabric

    {
        // Revert, should should look like a no-op
        fabricTable.RevertPendingFabricData();

        // No change of storage
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd);

        // Verify we can still see the fabric with the iterator
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }
    }
}

TEST_F(TestFabricTable, TestUpdateNocFailSafe)
{
    Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority;
    Credentials::TestOnlyLocalCertificateAuthority fabric44CertAuthority;

    chip::TestPersistentStorageDelegate storage;

    storage.SetLoggingLevel(chip::TestPersistentStorageDelegate::LoggingLevel::kLogMutation);

    EXPECT_TRUE(fabric11CertAuthority.Init().IsSuccess());
    EXPECT_TRUE(fabric44CertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    size_t numFabricsIterated = 0;

    size_t numStorageKeysAtStart = storage.GetNumKeys();

    // Sequence 1: Add node ID 999 on fabric 44, using operational keystore and ICAC --> Yield fabricIndex 1
    {
        FabricId fabricId = 44;
        NodeId nodeId     = 999;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric44CertAuthority.GetRcac();
        ByteSpan icac = fabric44CertAuthority.GetIcac();
        ByteSpan noc  = fabric44CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(newFabricIndex, 1);
        // No storage yet
        EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAtStart);

        // Commit, now storage should have keys
        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        EXPECT_EQ(storage.GetNumKeys(),
                  (numStorageKeysAtStart + 6)); // 3 opcerts + fabric metadata + 1 operational key + LKGT + fabric index

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), newFabricIndex);
        EXPECT_EQ(fabricInfo->GetNodeId(), nodeId);
        EXPECT_EQ(fabricInfo->GetFabricId(), fabricId);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

        // Validate that fabric has the correct operational key by verifying a signature
        {
            Crypto::P256ECDSASignature sig;
            uint8_t message[] = { 'm', 's', 'g' };

            Crypto::P256PublicKey nocPubKey;
            EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR);

            EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
            EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
        }

        // Verify we can now see the fabric with the iterator
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }
    }

    size_t numStorageAfterAdd = storage.GetNumKeys();

    // Sequence 2: Do an Update to NodeId 1000, with no ICAC, but revert it
    {
        FabricId fabricId       = 44;
        NodeId nodeId           = 1000;
        FabricIndex fabricIndex = 1;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };

        EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);

        // Make sure to tag fabric index to pending opkey: otherwise the UpdateNOC fails
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::MakeOptional(static_cast<FabricIndex>(1)), csrSpan),
                  CHIP_NO_ERROR);

        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric44CertAuthority.GetRcac();
        ByteSpan noc  = fabric44CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(1, noc, ByteSpan{}), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        // No storage yet
        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd);

        // Validate iterator sees the pending data
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 1000u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }

        // Revert, should see Node ID 999 again
        fabricTable.RevertPendingFabricData();
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.GetPendingNewFabricIndex(), kUndefinedFabricIndex);

        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        {
            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

            EXPECT_EQ(fabricInfo->GetFabricIndex(), fabricIndex);
            EXPECT_EQ(fabricInfo->GetNodeId(), 999u);
            EXPECT_EQ(fabricInfo->GetFabricId(), 44u);
            EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
            EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

            Crypto::P256PublicKey rootPublicKeyOfFabric;
            EXPECT_EQ(fabricTable.FetchRootPubkey(fabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
            EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));
        }

        // Validate that fabric has the correct operational key by verifying a signature
        {
            uint8_t nocBuf[Credentials::kMaxCHIPCertLength];
            MutableByteSpan nocSpan{ nocBuf };
            EXPECT_EQ(fabricTable.FetchNOCCert(1, nocSpan), CHIP_NO_ERROR);
            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(nocSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey nocPubKey(certificates.GetCertSet()[0].mPublicKey);

            Crypto::P256ECDSASignature sig;
            uint8_t message[] = { 'm', 's', 'g' };

            EXPECT_EQ(fabricTable.SignWithOpKeypair(fabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
            EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
        }

        // Validate iterator sees the previous fabric
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 999u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }
    }

    // Sequence 3: Do an Update to NodeId 1001, with no ICAC, but commit it
    {
        FabricId fabricId       = 44;
        NodeId nodeId           = 1001;
        FabricIndex fabricIndex = 1;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };

        // Make sure to tag fabric index to pending opkey: otherwise the UpdateNOC fails
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::MakeOptional(static_cast<FabricIndex>(1)), csrSpan),
                  CHIP_NO_ERROR);

        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric44CertAuthority.GetRcac();
        ByteSpan noc  = fabric44CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(1, noc, ByteSpan{}), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        // No storage yet
        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd);

        // Validate iterator sees the pending data
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 1001u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    EXPECT_TRUE(iterFabricInfo.ShouldAdvertiseIdentity());
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }

        // Commit, should see Node ID 1001, and 1 less cert in the storage
        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(storage.GetNumKeys(), numStorageAfterAdd - 1);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), fabricIndex);
        EXPECT_EQ(fabricInfo->GetNodeId(), 1001u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 44u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(fabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

        // Validate that fabric has the correct operational key by verifying a signature
        {
            Crypto::P256PublicKey nocPubKey;
            EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), nocPubKey), CHIP_NO_ERROR);

            Crypto::P256ECDSASignature sig;
            uint8_t message[] = { 'm', 's', 'g' };

            EXPECT_EQ(fabricTable.SignWithOpKeypair(fabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
            EXPECT_EQ(nocPubKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
        }

        // Validate iterator sees the updated fabric
        {
            numFabricsIterated = 0;
            bool saw1          = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 1001u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 44u);
                    saw1 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
        }
    }
}

TEST_F(TestFabricTable, TestAddRootCertFailSafe)
{
    Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority;

    chip::TestPersistentStorageDelegate storage;

    EXPECT_TRUE(fabric11CertAuthority.Init().IsSuccess());

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    // Add a root cert, see that pending works, and that revert works
    {
        ByteSpan rcac = fabric11CertAuthority.GetRcac();

        uint8_t rcacBuf[Credentials::kMaxCHIPCertLength];
        {
            // No pending root cert yet.
            MutableByteSpan fetchedSpan{ rcacBuf };
            EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_ERROR_NOT_FOUND);
        }

        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        {
            // Now have a pending root cert.
            MutableByteSpan fetchedSpan{ rcacBuf };
            EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_NO_ERROR);
            EXPECT_TRUE(fetchedSpan.data_equal(rcac));
        }

        // Revert
        fabricTable.RevertPendingFabricData();

        {
            // No pending root cert anymore.
            MutableByteSpan fetchedSpan{ rcacBuf };
            EXPECT_EQ(fabricTable.FetchPendingNonFabricAssociatedRootCert(fetchedSpan), CHIP_ERROR_NOT_FOUND);
        }
    }
}

TEST_F(TestFabricTable, TestSequenceErrors)
{
    // TODO: Write test
}

TEST_F(TestFabricTable, TestFabricLabelChange)
{
    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;

    chip::TestPersistentStorageDelegate storage;
    EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    // First scope: add FabricID 1111, node ID 55
    {
        FabricId fabricId = 1111;
        NodeId nodeId     = 55;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan rcac = fabricCertAuthority.GetRcac();
        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(newFabricIndex, 1);

        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);
    }
    size_t numStorageKeysAfterFirstAdd = storage.GetNumKeys();
    EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time

    // Second scope: set FabricLabel to "acme fabric", make sure it cannot be reverted
    {
        // Fabric label starts unset from prior scope
        CharSpan fabricLabel = "placeholder"_span;

        EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR);
        EXPECT_EQ(fabricLabel.size(), 0u);

        // Set a valid name
        EXPECT_EQ(fabricTable.SetFabricLabel(1, "acme fabric"_span), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR);
        EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span));

        // Revert pending fabric data. Should not revert name since nothing pending.
        fabricTable.RevertPendingFabricData();

        fabricLabel = "placeholder"_span;
        EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR);
        EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span));

        // Verify we fail to set too large a label (> kFabricLabelMaxLengthInBytes)
        CharSpan fabricLabelTooBig = "012345678901234567890123456789123456"_span;
        EXPECT_GT(fabricLabelTooBig.size(), chip::kFabricLabelMaxLengthInBytes);

        EXPECT_EQ(fabricTable.SetFabricLabel(1, fabricLabelTooBig), CHIP_ERROR_INVALID_ARGUMENT);

        fabricLabel = "placeholder"_span;
        EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR);
        EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span));
    }

    // Third scope: set fabric label after an update, it sticks, but then goes back after revert
    {
        FabricId fabricId = 1111;
        NodeId nodeId     = 66; // Update node ID from 55 to 66

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::MakeOptional(static_cast<FabricIndex>(1)), csrSpan),
                  CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(1, noc, icac), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        // Validate contents prior to change/revert
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);

        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_EQ(fabricInfo->GetNodeId(), 66u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);

        CharSpan fabricLabel = fabricInfo->GetFabricLabel();
        EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span));

        // Update fabric label
        fabricLabel = "placeholder"_span;
        EXPECT_EQ(fabricTable.SetFabricLabel(1, "roboto fabric"_span), CHIP_NO_ERROR);

        fabricLabel = "placeholder"_span;
        EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR);
        EXPECT_TRUE(fabricLabel.data_equal("roboto fabric"_span));

        // Revert pending fabric data. Should revert name to "acme fabric"
        fabricTable.RevertPendingFabricData();

        fabricLabel = "placeholder"_span;
        EXPECT_EQ(fabricTable.GetFabricLabel(1, fabricLabel), CHIP_NO_ERROR);
        EXPECT_TRUE(fabricLabel.data_equal("acme fabric"_span));
    }
}

TEST_F(TestFabricTable, TestCompressedFabricId)
{
    // TODO: Write test
}

TEST_F(TestFabricTable, TestFabricLookup)
{
    // Initialize a fabric table.
    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
    EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);
    EXPECT_EQ(LoadTestFabric_Node02_01(fabricTable, /* doCommit = */ true, FabricTable::AdvertiseIdentity::No), CHIP_NO_ERROR);

    // These two NOCs have the same fabric id on purpose; only the trust root is
    // different.
    constexpr FabricId kNode01_01_and_02_01_FabricId = 0xFAB000000000001D;

    // Attempt lookup of the Root01 fabric.
    {
        auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root01_PublicKey, kNode01_01_and_02_01_FabricId);
        ASSERT_NE(fabricInfo, nullptr);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());
    }

    // Attempt lookup of the Root02 fabric.
    {
        auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root02_PublicKey, kNode01_01_and_02_01_FabricId);
        ASSERT_NE(fabricInfo, nullptr);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
        EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity());
    }

    // Attempt lookup of FabricIndex 0 --> should always fail.
    {
        EXPECT_EQ(fabricTable.FindFabricWithIndex(0), nullptr);
    }
}

TEST_F(TestFabricTable, ShouldFailSetFabricIndexWithInvalidIndex)
{
    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(kUndefinedFabricIndex), CHIP_ERROR_INVALID_FABRIC_INDEX);
}

TEST_F(TestFabricTable, ShouldFailSetFabricIndexWithPendingFabric)
{
    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(ByteSpan(TestCerts::sTestCert_Root01_Chip)), CHIP_NO_ERROR);

    EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(1), CHIP_ERROR_INCORRECT_STATE);
}

TEST_F(TestFabricTable, ShouldFailSetFabricIndexWhenInUse)
{
    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);
    EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(1), CHIP_ERROR_FABRIC_EXISTS);
}

TEST_F(TestFabricTable, ShouldAddFabricAtRequestedIndex)
{
    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(2), CHIP_NO_ERROR);
    EXPECT_EQ(LoadTestFabric_Node02_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

    EXPECT_EQ(fabricTable.SetFabricIndexForNextAddition(1), CHIP_NO_ERROR);
    EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

    {
        auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root01_PublicKey, TestCerts::kTestCert_Node01_01_FabricId);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_EQ(fabricInfo->GetNodeId(), TestCerts::kTestCert_Node01_01_NodeId);
        EXPECT_EQ(fabricInfo->GetFabricId(), TestCerts::kTestCert_Node01_01_FabricId);
    }

    {
        auto fabricInfo = FindFabric(fabricTable, TestCerts::sTestCert_Root02_PublicKey, TestCerts::kTestCert_Node02_01_FabricId);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
        EXPECT_EQ(fabricInfo->GetNodeId(), TestCerts::kTestCert_Node02_01_NodeId);
        EXPECT_EQ(fabricInfo->GetFabricId(), TestCerts::kTestCert_Node02_01_FabricId);
    }
}

TEST_F(TestFabricTable, TestFetchCATs)
{
    // Initialize a fabric table.
    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
    EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);
    EXPECT_EQ(LoadTestFabric_Node02_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

    // Attempt Fetching fabric index 1 CATs and verify contents.
    {
        CATValues cats;
        EXPECT_EQ(fabricTable.FetchCATs(1, cats), CHIP_NO_ERROR);
        // Test fabric NOCs don't contain any CATs.
        EXPECT_EQ(cats, kUndefinedCATs);
    }

    // Attempt Fetching fabric index 2 CATs and verify contents.
    {
        CATValues cats;
        EXPECT_EQ(fabricTable.FetchCATs(2, cats), CHIP_NO_ERROR);
        // Test fabric NOCs don't contain any CATs.
        EXPECT_EQ(cats, kUndefinedCATs);
    }

    // TODO(#20335): Add test cases for NOCs that actually embed CATs
}

// Validate that adding the same fabric twice fails (same root, same FabricId)
TEST_F(TestFabricTable, TestAddNocRootCollision)
{
    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;

    chip::TestPersistentStorageDelegate storage;
    EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    // First scope: add FabricID 1111, node ID 55
    {
        FabricId fabricId = 1111;
        NodeId nodeId     = 55;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan rcac = fabricCertAuthority.GetRcac();
        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex,
                                                                         FabricTable::AdvertiseIdentity::No),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(newFabricIndex, 1);

        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_FALSE(fabricInfo->ShouldAdvertiseIdentity());

        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));
    }
    size_t numStorageKeysAfterFirstAdd = storage.GetNumKeys();
    EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time

    // Second scope: add FabricID 1111, node ID 55 *again* --> Collision of Root/FabricID with existing
    {
        FabricId fabricId = 1111;
        NodeId nodeId     = 55;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan rcac = fabricCertAuthority.GetRcac();
        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex),
                  CHIP_ERROR_FABRIC_EXISTS);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        CHIP_ERROR err = fabricTable.CommitPendingFabricData();
        printf("err = %" CHIP_ERROR_FORMAT "\n", err.Format());
        EXPECT_EQ(err, CHIP_ERROR_INCORRECT_STATE);

        // Validate contents of Fabric index 1 still valid
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(1, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));
    }

    // Ensure no new persisted keys after failed colliding add
    EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAfterFirstAdd);

    // Third scope: add FabricID 2222, node ID 55 --> Not colliding, should work. The failing commit above]
    // should have been enough of a revert that this scope succeeds without any additional revert.
    {
        FabricId fabricId = 2222;
        NodeId nodeId     = 55;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        ByteSpan rcac = fabricCertAuthority.GetRcac();
        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 2);
        EXPECT_EQ(newFabricIndex, 2);

        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_TRUE(fabricInfo->ShouldAdvertiseIdentity());

        Credentials::ChipCertificateSet certificates;
        EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
        EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
        Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

        EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
        EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 2222u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

        Crypto::P256PublicKey rootPublicKeyOfFabric;
        EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
        EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));
    }
    size_t numStorageKeysAfterSecondAdd = storage.GetNumKeys();

    EXPECT_EQ(numStorageKeysAfterSecondAdd, (numStorageKeysAfterFirstAdd + 5)); // Metadata, 3 certs, 1 opkey
}

TEST_F(TestFabricTable, TestInvalidChaining)
{
    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;
    Credentials::TestOnlyLocalCertificateAuthority differentCertAuthority;

    chip::TestPersistentStorageDelegate storage;
    EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess());
    EXPECT_TRUE(differentCertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    // Try to add fabric with either the NOC not chaining properly, or ICAC not chaining properly, fail,
    // then succeed with proper chaining
    {
        FabricId fabricId = 1111;
        NodeId nodeId     = 55;

        uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        // Generate same cert chain from two different roots
        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        EXPECT_EQ(differentCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);

        ByteSpan rcac = fabricCertAuthority.GetRcac();
        ByteSpan icac = fabricCertAuthority.GetIcac();
        ByteSpan noc  = fabricCertAuthority.GetNoc();

        ByteSpan otherIcac = differentCertAuthority.GetIcac();
        ByteSpan otherNoc  = differentCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.FabricCount(), 0);
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);

        // Add with NOC not chaining to ICAC: fail
        {
            FabricIndex newFabricIndex = kUndefinedFabricIndex;
            CHIP_ERROR err = fabricTable.AddNewPendingFabricWithOperationalKeystore(otherNoc, icac, kVendorId, &newFabricIndex);
            EXPECT_NE(err, CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 0);
        }

        // Add with ICAC not chaining to root: fail
        {
            FabricIndex newFabricIndex = kUndefinedFabricIndex;
            CHIP_ERROR err = fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, otherIcac, kVendorId, &newFabricIndex);
            EXPECT_NE(err, CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 0);
        }

        // Add with NOC and ICAC chaining together, but not to root: fail
        {
            FabricIndex newFabricIndex = kUndefinedFabricIndex;
            CHIP_ERROR err =
                fabricTable.AddNewPendingFabricWithOperationalKeystore(otherNoc, otherIcac, kVendorId, &newFabricIndex);
            EXPECT_NE(err, CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 0);
        }

        // Revert state, start tests without ICAC
        fabricTable.RevertPendingFabricData();

        // Generate same cert chain from two different roots

        csrSpan = MutableByteSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);
        EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
        EXPECT_EQ(differentCertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);

        rcac = fabricCertAuthority.GetRcac();
        noc  = fabricCertAuthority.GetNoc();

        otherNoc = differentCertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);

        // Add with NOC not chaining to RCAC: fail
        {
            FabricIndex newFabricIndex = kUndefinedFabricIndex;
            CHIP_ERROR err =
                fabricTable.AddNewPendingFabricWithOperationalKeystore(otherNoc, ByteSpan{}, kVendorId, &newFabricIndex);
            EXPECT_NE(err, CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 0);
        }

        // Add properly now
        FabricIndex newFabricIndex = kUndefinedFabricIndex;
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);
        EXPECT_EQ(newFabricIndex, 1);

        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

        // Validate contents
        const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
        ASSERT_NE(fabricInfo, nullptr);
        EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
        EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
        EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
        EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
        EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);
    }
}

TEST_F(TestFabricTable, TestEphemeralKeys)
{
    // Initialize a fabric table with operational keystore
    {
        chip::TestPersistentStorageDelegate storage;

        // Initialize a FabricTable
        ScopedFabricTable fabricTableHolder;
        EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
        FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

        Crypto::P256ECDSASignature sig;
        uint8_t message[] = { 'm', 's', 'g' };

        Crypto::P256Keypair * ephemeralKeypair = fabricTable.AllocateEphemeralKeypairForCASE();
        ASSERT_NE(ephemeralKeypair, nullptr);
        EXPECT_EQ(ephemeralKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA), CHIP_NO_ERROR);

        EXPECT_EQ(ephemeralKeypair->ECDSA_sign_msg(message, sizeof(message), sig), CHIP_NO_ERROR);
        EXPECT_EQ(ephemeralKeypair->Pubkey().ECDSA_validate_msg_signature(message, sizeof(message), sig), CHIP_NO_ERROR);

        fabricTable.ReleaseEphemeralKeypair(ephemeralKeypair);
    }

    // Use a fabric table without an operational keystore: should still work
    {
        chip::TestPersistentStorageDelegate storage;

        chip::Credentials::PersistentStorageOpCertStore opCertStore;
        EXPECT_EQ(opCertStore.Init(&storage), CHIP_NO_ERROR);

        FabricTable fabricTable;
        FabricTable::InitParams initParams;
        initParams.storage     = &storage;
        initParams.opCertStore = &opCertStore;

        EXPECT_EQ(fabricTable.Init(initParams), CHIP_NO_ERROR);

        Crypto::P256ECDSASignature sig;
        uint8_t message[] = { 'm', 's', 'g' };

        Crypto::P256Keypair * ephemeralKeypair = fabricTable.AllocateEphemeralKeypairForCASE();
        ASSERT_NE(ephemeralKeypair, nullptr);
        EXPECT_EQ(ephemeralKeypair->Initialize(Crypto::ECPKeyTarget::ECDSA), CHIP_NO_ERROR);

        EXPECT_EQ(ephemeralKeypair->ECDSA_sign_msg(message, sizeof(message), sig), CHIP_NO_ERROR);
        EXPECT_EQ(ephemeralKeypair->Pubkey().ECDSA_validate_msg_signature(message, sizeof(message), sig), CHIP_NO_ERROR);

        fabricTable.ReleaseEphemeralKeypair(ephemeralKeypair);

        fabricTable.Shutdown();
        opCertStore.Finish();
    }
}

TEST_F(TestFabricTable, TestCommitMarker)
{
    Crypto::P256PublicKey fIdx1PublicKey;
    Crypto::P256PublicKey fIdx2PublicKey;

    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;

    chip::TestPersistentStorageDelegate storage;

    // Log verbosity on this test helps debug significantly
    storage.SetLoggingLevel(chip::TestPersistentStorageDelegate::LoggingLevel::kLogMutationAndReads);

    EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess());

    constexpr uint16_t kVendorId = 0xFFF1u;

    size_t numStorageKeysAfterFirstAdd = 0;

    // First scope: add 2 fabrics with same root:
    //   - FabricID 1111, Node ID 55
    //   - FabricID 2222, Node ID 66
    //      - Abort commit on second fabric
    {
        // Initialize a fabric table
        ScopedFabricTable fabricTableHolder;
        EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
        FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

        EXPECT_EQ(fabricTable.GetDeletedFabricFromCommitMarker(), kUndefinedFabricIndex);
        EXPECT_EQ(fabricTable.FabricCount(), 0);

        // Add Fabric 1111 Node Id 55
        {
            FabricId fabricId = 1111;
            NodeId nodeId     = 55;

            uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
            MutableByteSpan csrSpan{ csrBuf };
            EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

            EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                      CHIP_NO_ERROR);
            ByteSpan rcac = fabricCertAuthority.GetRcac();
            ByteSpan icac = fabricCertAuthority.GetIcac();
            ByteSpan noc  = fabricCertAuthority.GetNoc();

            EXPECT_EQ(fabricTable.FabricCount(), 0);
            EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
            FabricIndex newFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 1);
            EXPECT_EQ(newFabricIndex, 1);

            EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);

            // Validate contents
            const auto * fabricInfo = fabricTable.FindFabricWithIndex(1);
            ASSERT_NE(fabricInfo, nullptr);
            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

            EXPECT_EQ(fabricInfo->GetFabricIndex(), 1);
            EXPECT_EQ(fabricInfo->GetNodeId(), 55u);
            EXPECT_EQ(fabricInfo->GetFabricId(), 1111u);
            EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
            EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

            Crypto::P256PublicKey rootPublicKeyOfFabric;
            EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
            EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

            // Validate that fabric has the correct operational key by verifying a signature
            {
                Crypto::P256ECDSASignature sig;
                uint8_t message[] = { 'm', 's', 'g' };

                EXPECT_EQ(VerifyCertificateSigningRequest(csrSpan.data(), csrSpan.size(), fIdx1PublicKey), CHIP_NO_ERROR);

                EXPECT_EQ(fabricTable.SignWithOpKeypair(newFabricIndex, ByteSpan{ message }, sig), CHIP_NO_ERROR);
                EXPECT_EQ(fIdx1PublicKey.ECDSA_validate_msg_signature(&message[0], sizeof(message), sig), CHIP_NO_ERROR);
            }
        }
        numStorageKeysAfterFirstAdd = storage.GetNumKeys();

        EXPECT_EQ(numStorageKeysAfterFirstAdd, 7u); // Metadata, index, 3 certs, 1 opkey, last known good time

        // The following test requires test methods not available on all builds.
        // TODO: Debug why some CI jobs don't set it properly.
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST

        // Add Fabric 2222 Node Id 66, no ICAC *** AND ABORT COMMIT ***
        {
            FabricId fabricId = 2222;
            NodeId nodeId     = 66;

            uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
            MutableByteSpan csrSpan{ csrBuf };
            EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

            EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                      CHIP_NO_ERROR);
            ByteSpan rcac = fabricCertAuthority.GetRcac();
            ByteSpan noc  = fabricCertAuthority.GetNoc();

            EXPECT_EQ(fabricTable.FabricCount(), 1);
            EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
            FabricIndex newFabricIndex = kUndefinedFabricIndex;
            EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, ByteSpan{}, kVendorId, &newFabricIndex),
                      CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 2);
            EXPECT_EQ(newFabricIndex, 2);

            // Validate contents of pending
            const auto * fabricInfo = fabricTable.FindFabricWithIndex(2);
            ASSERT_NE(fabricInfo, nullptr);
            Credentials::ChipCertificateSet certificates;
            EXPECT_EQ(certificates.Init(1), CHIP_NO_ERROR);
            EXPECT_EQ(certificates.LoadCert(rcac, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)), CHIP_NO_ERROR);
            Crypto::P256PublicKey rcacPublicKey(certificates.GetCertSet()[0].mPublicKey);

            EXPECT_EQ(fabricInfo->GetFabricIndex(), 2);
            EXPECT_EQ(fabricInfo->GetNodeId(), 66u);
            EXPECT_EQ(fabricInfo->GetFabricId(), 2222u);
            EXPECT_EQ(fabricInfo->GetVendorId(), kVendorId);
            EXPECT_EQ(fabricInfo->GetFabricLabel().size(), 0u);

            Crypto::P256PublicKey rootPublicKeyOfFabric;
            EXPECT_EQ(fabricTable.FetchRootPubkey(newFabricIndex, rootPublicKeyOfFabric), CHIP_NO_ERROR);
            EXPECT_TRUE(rootPublicKeyOfFabric.Matches(rcacPublicKey));

            // Make sure no additional storage yet
            EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAfterFirstAdd);

            // --> FORCE AN ERROR ON COMMIT that will BYPASS commit clean-up (similar to reboot during commit)
            fabricTable.SetForceAbortCommitForTest(true);
            EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_ERROR_INTERNAL);

            // Check that there are more keys now, partially committed: at least a Commit Marker (+1)
            // and some more keys from the aborted process.
            EXPECT_GT(storage.GetNumKeys(), (numStorageKeysAfterFirstAdd + 1));
        }
#endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
    }

#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
    {
        storage.DumpKeys();

        // Initialize a FabricTable again. Make sure it succeeds in initing.
        ScopedFabricTable fabricTableHolder;

        EXPECT_GT(storage.GetNumKeys(), (numStorageKeysAfterFirstAdd + 1));

        EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
        FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

        // Make sure that after init, the fabricTable has only 1 fabric
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        // Make sure it caught the last partially committed fabric
        EXPECT_EQ(fabricTable.GetDeletedFabricFromCommitMarker(), 2);

        // Second read must return kUndefinedFabricIndex
        EXPECT_EQ(fabricTable.GetDeletedFabricFromCommitMarker(), kUndefinedFabricIndex);

        {
            // Here we would do other clean-ups (e.g. see Server.cpp that uses the above) and then
            // clear the commit marker after.
            fabricTable.ClearCommitMarker();
        }

        // Make sure that all other pending storage got deleted
        EXPECT_EQ(storage.GetNumKeys(), numStorageKeysAfterFirstAdd);

        // Verify we can only see 1 fabric with the iterator
        {
            size_t numFabricsIterated = 0;
            bool saw1                 = false;
            bool saw2                 = false;
            for (const auto & iterFabricInfo : fabricTable)
            {
                ++numFabricsIterated;
                if (iterFabricInfo.GetFabricIndex() == 1)
                {
                    EXPECT_EQ(iterFabricInfo.GetNodeId(), 55u);
                    EXPECT_EQ(iterFabricInfo.GetFabricId(), 1111u);
                    saw1 = true;
                }
                if (iterFabricInfo.GetFabricIndex() == 2)
                {
                    saw2 = true;
                }
            }

            EXPECT_EQ(numFabricsIterated, 1u);
            EXPECT_TRUE(saw1);
            EXPECT_FALSE(saw2);
        }
    }
#endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
}

class TestFabricTableDelegate : public FabricTable::Delegate
{
public:
    void FabricWillBeRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override { willBeRemovedCalled = true; }

    void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override { onRemovedCalled = true; }

    bool willBeRemovedCalled = false;
    bool onRemovedCalled     = false;
};

TEST_F(TestFabricTable, DeleteFabricCallsDelegate)
{
    Credentials::TestOnlyLocalCertificateAuthority fabricCertAuthority;
    EXPECT_TRUE(fabricCertAuthority.Init().IsSuccess());

    chip::TestPersistentStorageDelegate storage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);

    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();
    FabricId fabricId         = 1;
    NodeId nodeId             = 10;

    // Simulate AddNOC
    uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];
    MutableByteSpan csrSpan{ csrBuf };
    EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

    EXPECT_EQ(fabricCertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(), CHIP_NO_ERROR);
    ByteSpan rcac = fabricCertAuthority.GetRcac();
    ByteSpan icac = fabricCertAuthority.GetIcac();
    ByteSpan noc  = fabricCertAuthority.GetNoc();

    EXPECT_SUCCESS(fabricTable.AddNewPendingTrustedRootCert(rcac));

    constexpr uint16_t kVendorId = 0xFFF1u;
    FabricIndex newFabricIndex   = kUndefinedFabricIndex;
    EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, icac, kVendorId, &newFabricIndex), CHIP_NO_ERROR);

    // Reinitialize FabricTable to simulate device reboot
    EXPECT_EQ(fabricTableHolder.ReinitFabricTable(&storage), CHIP_NO_ERROR);

    TestFabricTableDelegate fabricDelegate;
    EXPECT_SUCCESS(fabricTable.AddFabricDelegate(&fabricDelegate));

    // Check if calling Delete invokes OnFabricRemoved on delegates
    EXPECT_EQ(fabricTable.Delete(newFabricIndex), CHIP_ERROR_NOT_FOUND);
    EXPECT_TRUE(fabricDelegate.willBeRemovedCalled);
    EXPECT_TRUE(fabricDelegate.onRemovedCalled);

    fabricTable.RemoveFabricDelegate(&fabricDelegate);
}

TEST_F(TestFabricTable, VidVerificationSigningWorksWithoutVvs)
{
    chip::TestPersistentStorageDelegate storage;
    Crypto::P256SerializedKeypair rootKeyForTestSerialized;

    // Setup a known key so that test vectors are also known and validated.
    // DO NOT CHANGE WITHOUT RE-AUDITING THE TEST VECTORS!
    {
        static const uint8_t kTestCert_Root02_PublicKey_Buffer[] = {
            0x04, 0x27, 0x50, 0x0b, 0x20, 0x60, 0x52, 0xce, 0x33, 0x77, 0x6c, 0x63, 0x08, 0x3f, 0x1c, 0xf1, 0x03,
            0x6e, 0xa4, 0xcc, 0x7f, 0xfd, 0x61, 0x7c, 0x17, 0x6d, 0x4c, 0xad, 0xf5, 0x51, 0xbb, 0xb4, 0xb0, 0xd9,
            0x97, 0xca, 0xe5, 0x55, 0xdb, 0xf9, 0xbc, 0xa8, 0x56, 0xe4, 0xcc, 0x7a, 0x8e, 0xde, 0x91, 0xe0, 0xa7,
            0x33, 0xe1, 0x67, 0xc0, 0x41, 0x67, 0xa6, 0xc2, 0xc9, 0xfa, 0x48, 0xf1, 0x4f, 0x0b,
        };
        const ByteSpan kTestCert_Root02_PublicKey{ kTestCert_Root02_PublicKey_Buffer };

        static const uint8_t kTestCert_Root02_PrivateKey_Buffer[] = {
            0xf9, 0xb7, 0x22, 0x87, 0xd6, 0xa4, 0x8d, 0xd5, 0x10, 0x93, 0xc4, 0x12, 0xe6, 0x1b, 0x43, 0xaf,
            0xe1, 0x22, 0x15, 0x28, 0x96, 0x71, 0xbf, 0x33, 0x96, 0xff, 0x97, 0xf6, 0xb6, 0x21, 0xb2, 0xac,
        };
        const ByteSpan kTestCert_Root02_PrivateKey{ kTestCert_Root02_PrivateKey_Buffer };

        memcpy(rootKeyForTestSerialized.Bytes(), kTestCert_Root02_PublicKey.data(), kTestCert_Root02_PublicKey.size());
        memcpy(rootKeyForTestSerialized.Bytes() + kTestCert_Root02_PublicKey.size(), kTestCert_Root02_PrivateKey.data(),
               kTestCert_Root02_PrivateKey.size());
        EXPECT_SUCCESS(rootKeyForTestSerialized.SetLength(rootKeyForTestSerialized.Capacity()));
    }

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    // Add a basic fabric, don't care about NodeID.
    constexpr FabricId kFabricId1 = 0x1122334455667788;
    constexpr uint16_t kVendorId1 = 0xFFF1u;

    {
        FabricIndex fabricIndex1 =
            CreateBasicFabricForVidVerification(fabricTable, rootKeyForTestSerialized, kFabricId1, kVendorId1);
        ASSERT_EQ(fabricIndex1, 1u);
        ASSERT_EQ(fabricTable.FabricCount(), 1u);
    }

    // Add a second basic fabric, don't care about NodeID.
    constexpr FabricId kFabricId2 = 0xA1A2A3A4A5A6A7A8;
    constexpr uint16_t kVendorId2 = 0xFFF2u;
    {
        FabricIndex fabricIndex2 =
            CreateBasicFabricForVidVerification(fabricTable, rootKeyForTestSerialized, kFabricId2, kVendorId2);
        ASSERT_EQ(fabricIndex2, 2u);
        ASSERT_EQ(fabricTable.FabricCount(), 2u);
    }

    // Ask for SignVIDVerification for both fabrics.
    FabricTable::SignVIDVerificationResponseData responseData1;
    {
        FabricIndex kFabricIndex1 = 1u;

        const uint8_t kAttestationChallenge1[16] = {
            0x90, 0x4b, 0x82, 0xe6, 0x6e, 0x98, 0x57, 0x85, 0xb4, 0xa3, 0xff, 0x18, 0xc2, 0x59, 0x47, 0xf3,
        };
        ByteSpan kAttestationChallengeSpan1{ kAttestationChallenge1 };

        const uint8_t kClientChallenge1[32] = {
            0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0,
            0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
        };
        ByteSpan kClientChallengeSpan1{ kClientChallenge1 };

        ASSERT_EQ(
            fabricTable.SignVIDVerificationRequest(kFabricIndex1, kClientChallengeSpan1, kAttestationChallengeSpan1, responseData1),
            CHIP_NO_ERROR);

        EXPECT_EQ(responseData1.fabricBindingVersion, to_underlying(Crypto::FabricBindingVersion::kVersion1));
        EXPECT_EQ(responseData1.fabricIndex, kFabricIndex1);

        const uint8_t kExpectedUnderlyingTbs1[126] = {
            0x01, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1,
            0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x90, 0x4b, 0x82,
            0xe6, 0x6e, 0x98, 0x57, 0x85, 0xb4, 0xa3, 0xff, 0x18, 0xc2, 0x59, 0x47, 0xf3, 0x01, 0x01, 0x04, 0x27, 0x50,
            0x0b, 0x20, 0x60, 0x52, 0xce, 0x33, 0x77, 0x6c, 0x63, 0x08, 0x3f, 0x1c, 0xf1, 0x03, 0x6e, 0xa4, 0xcc, 0x7f,
            0xfd, 0x61, 0x7c, 0x17, 0x6d, 0x4c, 0xad, 0xf5, 0x51, 0xbb, 0xb4, 0xb0, 0xd9, 0x97, 0xca, 0xe5, 0x55, 0xdb,
            0xf9, 0xbc, 0xa8, 0x56, 0xe4, 0xcc, 0x7a, 0x8e, 0xde, 0x91, 0xe0, 0xa7, 0x33, 0xe1, 0x67, 0xc0, 0x41, 0x67,
            0xa6, 0xc2, 0xc9, 0xfa, 0x48, 0xf1, 0x4f, 0x0b, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xff, 0xf1,
        };

        EXPECT_EQ(VerifySignatureWithNocPublicKey(fabricTable, kFabricIndex1, ByteSpan{ kExpectedUnderlyingTbs1 },
                                                  responseData1.signature.Span()),
                  CHIP_NO_ERROR);
    }

    FabricTable::SignVIDVerificationResponseData responseData2;
    {
        FabricIndex kFabricIndex2 = 2u;

        const uint8_t kAttestationChallenge2[16] = {
            0x14, 0x5f, 0x2e, 0xf3, 0x9e, 0x3e, 0x4f, 0xfc, 0xf5, 0x64, 0x9c, 0x09, 0x09, 0xcb, 0xc8, 0xe4,
        };
        ByteSpan kAttestationChallengeSpan2{ kAttestationChallenge2 };

        const uint8_t kClientChallenge2[32] = {
            0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
            0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0,
        };
        ByteSpan kClientChallengeSpan2{ kClientChallenge2 };

        ASSERT_EQ(
            fabricTable.SignVIDVerificationRequest(kFabricIndex2, kClientChallengeSpan2, kAttestationChallengeSpan2, responseData2),
            CHIP_NO_ERROR);

        EXPECT_EQ(responseData2.fabricBindingVersion, to_underlying(Crypto::FabricBindingVersion::kVersion1));
        EXPECT_EQ(responseData2.fabricIndex, kFabricIndex2);

        const uint8_t kExpectedUnderlyingTbs2[126] = {
            0x01, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1,
            0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0x14, 0x5f, 0x2e,
            0xf3, 0x9e, 0x3e, 0x4f, 0xfc, 0xf5, 0x64, 0x9c, 0x09, 0x09, 0xcb, 0xc8, 0xe4, 0x02, 0x01, 0x04, 0x27, 0x50,
            0x0b, 0x20, 0x60, 0x52, 0xce, 0x33, 0x77, 0x6c, 0x63, 0x08, 0x3f, 0x1c, 0xf1, 0x03, 0x6e, 0xa4, 0xcc, 0x7f,
            0xfd, 0x61, 0x7c, 0x17, 0x6d, 0x4c, 0xad, 0xf5, 0x51, 0xbb, 0xb4, 0xb0, 0xd9, 0x97, 0xca, 0xe5, 0x55, 0xdb,
            0xf9, 0xbc, 0xa8, 0x56, 0xe4, 0xcc, 0x7a, 0x8e, 0xde, 0x91, 0xe0, 0xa7, 0x33, 0xe1, 0x67, 0xc0, 0x41, 0x67,
            0xa6, 0xc2, 0xc9, 0xfa, 0x48, 0xf1, 0x4f, 0x0b, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xff, 0xf2,
        };

        EXPECT_EQ(VerifySignatureWithNocPublicKey(fabricTable, kFabricIndex2, ByteSpan{ kExpectedUnderlyingTbs2 },
                                                  responseData2.signature.Span()),
                  CHIP_NO_ERROR);
    }
}

TEST_F(TestFabricTable, JFVidVerificationWorksWithoutVvsUsingTestCerts)
{
    chip::TestPersistentStorageDelegate storage;

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    FabricIndex kFabricIndex = kUndefinedFabricIndex;
    EXPECT_SUCCESS(fabricTable.AddNewFabricForTestIgnoringCollisions(
        TestCerts::GetJFBRootCertAsset().mCert, TestCerts::GetJFBIACertAsset().mCert, TestCerts::GetJFBNodeCertAsset().mCert,
        TestCerts::GetJFBNodeCertAsset().mKey, &kFabricIndex));

    EXPECT_EQ(fabricTable.FabricCount(), 1);

    const FabricInfo * fabricInfo = fabricTable.FindFabricWithIndex(kFabricIndex);
    VendorId kVendorId            = fabricInfo->GetVendorId();

    FabricTable::SignVIDVerificationResponseData responseData;
    {
        const uint8_t kAttestationChallenge[16] = {
            0x14, 0x5f, 0x2e, 0xf3, 0x9e, 0x3e, 0x4f, 0xfc, 0xf5, 0x64, 0x9c, 0x09, 0x09, 0xcb, 0xc8, 0xe4,
        };
        ByteSpan kAttestationChallengeSpan{ kAttestationChallenge };

        const uint8_t kClientChallenge[32] = {
            0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0,
            0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0,
        };
        ByteSpan kClientChallengeSpan{ kClientChallenge };

        uint8_t rcacBuf[Credentials::kMaxCHIPCertLength];
        MutableByteSpan rcacSpan{ rcacBuf };
        ASSERT_EQ(fabricTable.FetchRootCert(kFabricIndex, rcacSpan), CHIP_NO_ERROR);
        Credentials::P256PublicKeySpan kTrustedCAPublicKeySpan;
        ASSERT_EQ(Credentials::ExtractPublicKeyFromChipCert(rcacSpan, kTrustedCAPublicKeySpan), CHIP_NO_ERROR);

        uint8_t kVendorFabricBindingMessageBuffer[Crypto::kVendorFabricBindingMessageV1Size];
        MutableByteSpan kVendorFabricBindingMessageSpan{ kVendorFabricBindingMessageBuffer };
        ASSERT_EQ(Crypto::GenerateVendorFabricBindingMessage(Crypto::FabricBindingVersion::kVersion1, kTrustedCAPublicKeySpan,
                                                             kFabricIndex, kVendorId, kVendorFabricBindingMessageSpan),
                  CHIP_NO_ERROR);

        ByteSpan kVidVerificationStatementSpan;
        uint8_t kExpectedUnderlyingTbsBuffer[Crypto::kVendorIdVerificationTbsV1MaxSize];
        MutableByteSpan kExpectedUnderlyingTbs{ kExpectedUnderlyingTbsBuffer };
        ASSERT_EQ(Crypto::GenerateVendorIdVerificationToBeSigned(kFabricIndex, kClientChallengeSpan, kAttestationChallengeSpan,
                                                                 kVendorFabricBindingMessageSpan, kVidVerificationStatementSpan,
                                                                 kExpectedUnderlyingTbs),
                  CHIP_NO_ERROR);

        ASSERT_EQ(
            fabricTable.SignVIDVerificationRequest(kFabricIndex, kClientChallengeSpan, kAttestationChallengeSpan, responseData),
            CHIP_NO_ERROR);

        EXPECT_EQ(responseData.fabricBindingVersion, to_underlying(Crypto::FabricBindingVersion::kVersion1));
        EXPECT_EQ(responseData.fabricIndex, kFabricIndex);

        EXPECT_EQ(VerifySignatureWithNocPublicKey(fabricTable, kFabricIndex, ByteSpan{ kExpectedUnderlyingTbs },
                                                  responseData.signature.Span()),
                  CHIP_NO_ERROR);
    }
}

TEST_F(TestFabricTable, SettingVvscFailsWithIcacAndSucceedWithout)
{
    chip::TestPersistentStorageDelegate storage;
    Crypto::P256SerializedKeypair rootKeyForTestSerialized;

    chip::TestPersistentStorageDelegate testStorage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&testStorage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    // We are doing logic checks one fabric at a time, agains the FabricTable. Therefore
    // it's OK to have 2 fabrics with same <root, fabric ID> together in fabric table to
    // test logic simpler.

    // Has ICAC, will be FabricIndex 1.
    fabricTable.PermitCollidingFabrics();
    EXPECT_EQ(LoadTestFabric_Node01_01(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

    // Does not have ICAC, will be Fabric Index 2.
    fabricTable.PermitCollidingFabrics();
    EXPECT_EQ(LoadTestFabric_Node01_02(fabricTable, /* doCommit = */ true), CHIP_NO_ERROR);

    ASSERT_NE(fabricTable.FindFabricWithIndex(1), nullptr);
    ASSERT_NE(fabricTable.FindFabricWithIndex(2), nullptr);

    uint8_t vvsc[kMaxCHIPCertLength];
    memset(&vvsc[0], 0xff, sizeof(vvsc));
    ByteSpan vvscSpan{ vvsc };

    uint8_t vvs[Crypto::kVendorIdVerificationStatementV1Size];
    memset(&vvs[0], 0x01, sizeof(vvs));
    ByteSpan vvsSpan{ vvs };

    ByteSpan emptyVvscSpan{};
    ByteSpan emptyVvsSpan{};

    // Try to set VVSC when ICAC set --> failure.
    {
        bool fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 1, /* vendorId = */ chip::NullOptional,
                                                                  NullOptional, chip::MakeOptional<>(vvscSpan),
                                                                  fabricTableWasChanged),
                  CHIP_ERROR_INCORRECT_STATE);
        EXPECT_EQ(fabricTableWasChanged, false);
    }

    // Try to set VVSC when ICAC not set --> success.
    {
        bool fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
                                                                  NullOptional, chip::MakeOptional<>(vvscSpan),
                                                                  fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, false);

        // Change a byte, should succeed again.
        vvsc[1]               = 0x00;
        fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
                                                                  NullOptional, chip::MakeOptional<>(vvscSpan),
                                                                  fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, false);

        // The VVSC should match by the end.
        uint8_t actualVvsc[kMaxCHIPCertLength];
        MutableByteSpan actualVvscSpan{ actualVvsc };
        EXPECT_EQ(
            fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                /* fabricIndex = */ 2, Credentials::OperationalCertificateStore::VidVerificationElement::kVvsc, actualVvscSpan),
            CHIP_NO_ERROR);
        EXPECT_TRUE(actualVvscSpan.data_equal(vvscSpan));

        // Erase VVSC with empty span.
        fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
                                                                  NullOptional, chip::MakeOptional<>(emptyVvscSpan),
                                                                  fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, false);

        // The VVSC should be empty now.
        actualVvscSpan = MutableByteSpan{ actualVvsc };
        EXPECT_EQ(
            fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                /* fabricIndex = */ 2, Credentials::OperationalCertificateStore::VidVerificationElement::kVvsc, actualVvscSpan),
            CHIP_NO_ERROR);
        EXPECT_TRUE(actualVvscSpan.empty());
    }

    // VVS updating flow. Always works whether ICAC present or not.
    {
        bool fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
                                                                  chip::MakeOptional<>(vvsSpan), NullOptional,
                                                                  fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, true);

        // Re-set VVS, should succeed again, but no fabric table changes seen.
        fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
                                                                  chip::MakeOptional<>(vvsSpan), NullOptional,
                                                                  fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, false);

        // Change a byte, should succeed again, but changes notifed.
        vvs[1]                = 0x02;
        fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
                                                                  chip::MakeOptional<>(vvsSpan), NullOptional,
                                                                  fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, true);

        // The VVS should match by the end.
        uint8_t actualVvs[Crypto::kVendorIdVerificationStatementV1Size];
        MutableByteSpan actualVvsSpan{ actualVvs };
        EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                      /* fabricIndex = */ 2,
                      Credentials::OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement, actualVvsSpan),
                  CHIP_NO_ERROR);
        EXPECT_TRUE(actualVvsSpan.data_equal(vvsSpan));

        // Erase VVS with empty span.
        fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(/* fabricIndex = */ 2, /* vendorId = */ chip::NullOptional,
                                                                  chip::MakeOptional<>(emptyVvsSpan), NullOptional,
                                                                  fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, true);

        // The VVS should be empty now.
        actualVvsSpan = MutableByteSpan{ actualVvs };
        EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                      /* fabricIndex = */ 2,
                      Credentials::OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement, actualVvsSpan),
                  CHIP_NO_ERROR);
        EXPECT_TRUE(actualVvsSpan.empty());
    }
}

TEST_F(TestFabricTable, UpdateNocWithIcacFailsWithVvscSucceedsWithout)
{
    // Credentials::TestOnlyLocalCertificateAuthority fabric11CertAuthority;
    Credentials::TestOnlyLocalCertificateAuthority fabric44CertAuthority;
    EXPECT_TRUE(fabric44CertAuthority.Init().IsSuccess());

    // Initialize a fabric table.
    chip::TestPersistentStorageDelegate storage;
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    constexpr uint16_t kVendorId    = 0xFFF1u;
    constexpr uint16_t kNewVendorId = 0xFFF2u;
    FabricIndex newFabricIndex      = kUndefinedFabricIndex;

    // VVSC contents is not checked, so can be just zero bytes.
    uint8_t vvsc[kMaxCHIPCertLength];
    memset(&vvsc[0], 0x00, sizeof(vvsc));
    ByteSpan vvscSpan{ vvsc };

    // VVS contents is not checked, except first by that must be 0x01.
    uint8_t vvs[Crypto::kVendorIdVerificationStatementV1Size];
    memset(&vvs[0], 0x01, sizeof(vvs));
    ByteSpan vvsSpan{ vvs };

    uint8_t csrBuf[chip::Crypto::kMIN_CSR_Buffer_Size];

    // Fabric Index 1: Add node ID 33 on fabric 44, using operational keystore and no ICAC. Set a default VVSC.
    {
        FabricId fabricId = 44;
        NodeId nodeId     = 333;

        MutableByteSpan csrSpan{ csrBuf };
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(chip::NullOptional, csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan rcac = fabric44CertAuthority.GetRcac();
        ByteSpan noc  = fabric44CertAuthority.GetNoc();
        EXPECT_EQ(fabricTable.AddNewPendingTrustedRootCert(rcac), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.AddNewPendingFabricWithOperationalKeystore(noc, /*icac = */ ByteSpan{}, kVendorId, &newFabricIndex),
                  CHIP_NO_ERROR);
        EXPECT_EQ(newFabricIndex, 1u);
        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);

        bool fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(newFabricIndex, NullOptional, NullOptional,
                                                                  MakeOptional(vvscSpan), fabricTableWasChanged),
                  CHIP_NO_ERROR);
        // Changing VVSC doesn't change Fabrics table itself.
        EXPECT_EQ(fabricTableWasChanged, false);

        // Make sure VVSC was stored.
        {
            uint8_t readBackVvscBuf[kMaxCHIPCertLength];
            memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
            MutableByteSpan readBackVvscSpan{ readBackVvscBuf };

            EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                          newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
                      CHIP_NO_ERROR);
            EXPECT_TRUE(readBackVvscSpan.data_equal(vvscSpan));
        }
    }

    // Try to update node ID to 33 on fabric 44 WITHOUT ICAC. Must succeed even if VVSC present.
    // Then remove VVSC, and update NOC chain to include ICAC, and this should work.
    // Then update NOC chain to WITHOUT ICAC, and add VVSC and VVS while pending. Then revert. Should go back to prior state.
    {
        MutableByteSpan csrSpan{ csrBuf };
        FabricIndex existingFabricIndex = 1u;

        // Update to NodeID 33 with ICAC
        {
            FabricId fabricId = 44;
            NodeId nodeId     = 33;

            ASSERT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
            EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                      CHIP_NO_ERROR);
            ByteSpan noc = fabric44CertAuthority.GetNoc();

            EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, ByteSpan{},
                                                                             FabricTable::AdvertiseIdentity::No),
                      CHIP_NO_ERROR);
            ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), fabricId);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), nodeId);
            EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 1);
        }

        // Remove VVSC and update to NodeID 66 WITH ICAC
        {
            {
                bool fabricTableWasChanged = false;
                EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(newFabricIndex, NullOptional, NullOptional,
                                                                          MakeOptional(ByteSpan{}), fabricTableWasChanged),
                          CHIP_NO_ERROR);
                // Changing VVSC doesn't change Fabrics table itself.
                EXPECT_EQ(fabricTableWasChanged, false);

                // Make sure VVSC appears removed.
                {
                    uint8_t readBackVvscBuf[kMaxCHIPCertLength];
                    memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
                    MutableByteSpan readBackVvscSpan{ readBackVvscBuf };

                    EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                                  newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
                              CHIP_NO_ERROR);
                    EXPECT_TRUE(readBackVvscSpan.empty());
                }
            }

            FabricId fabricId = 44;
            NodeId nodeId     = 66;

            csrSpan = MutableByteSpan{ csrBuf };
            ASSERT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
            EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                      CHIP_NO_ERROR);
            ByteSpan icac = fabric44CertAuthority.GetIcac();
            ByteSpan noc  = fabric44CertAuthority.GetNoc();

            EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, icac,
                                                                             FabricTable::AdvertiseIdentity::No),
                      CHIP_NO_ERROR);
            ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), fabricId);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), nodeId);
            EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
            EXPECT_EQ(fabricTable.FabricCount(), 1);
        }

        // Update to Node ID 88 without ICAC. Set VVS/VVSC/VendorID before reverting. Revert and make sure VVSC/VVS/VendorID are
        // reverted.
        {
            FabricId fabricId = 44;
            NodeId nodeId     = 88;

            csrSpan = MutableByteSpan{ csrBuf };
            ASSERT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
            EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                      CHIP_NO_ERROR);
            ByteSpan noc = fabric44CertAuthority.GetNoc();

            EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, ByteSpan{},
                                                                             FabricTable::AdvertiseIdentity::No),
                      CHIP_NO_ERROR);
            ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), fabricId);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), nodeId);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetVendorId(), kVendorId);

            // Set new pending VVS, VVSC and VendorId.
            bool fabricTableWasChanged = false;
            EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(newFabricIndex, MakeOptional(kNewVendorId),
                                                                      MakeOptional(vvsSpan), MakeOptional(vvscSpan),
                                                                      fabricTableWasChanged),
                      CHIP_NO_ERROR);
            EXPECT_EQ(fabricTableWasChanged, true);

            // Make sure VVSC was set pending.
            {
                uint8_t readBackVvscBuf[kMaxCHIPCertLength];
                memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
                MutableByteSpan readBackVvscSpan{ readBackVvscBuf };

                EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                              newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
                          CHIP_NO_ERROR);
                EXPECT_TRUE(readBackVvscSpan.data_equal(vvscSpan));
            }

            // Make sure VVS was also set pending
            {
                uint8_t readBackVvsBuf[Crypto::kVendorIdVerificationStatementV1Size];
                memset(&readBackVvsBuf[0], 0x00, sizeof(readBackVvsBuf));
                MutableByteSpan readBackVvsSpan{ readBackVvsBuf };

                EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                              newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement,
                              readBackVvsSpan),
                          CHIP_NO_ERROR);
                EXPECT_TRUE(readBackVvsSpan.data_equal(vvsSpan));
            }

            ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetVendorId(), kNewVendorId);

            // Revert state, expect previous fabric data, and empty VVSC/VVS again.
            fabricTable.RevertPendingFabricData();

            EXPECT_EQ(fabricTable.FabricCount(), 1);
            ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), 44u);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), 66u);
            EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetVendorId(), kVendorId);

            // Make sure VVSC got reverted.
            {
                uint8_t readBackVvscBuf[kMaxCHIPCertLength];
                memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
                MutableByteSpan readBackVvscSpan{ readBackVvscBuf };

                EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                              newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
                          CHIP_NO_ERROR);
                EXPECT_TRUE(readBackVvscSpan.empty());
            }

            // Make sure VVS got reverted.
            {
                uint8_t readBackVvsBuf[Crypto::kVendorIdVerificationStatementV1Size];
                memset(&readBackVvsBuf[0], 0x00, sizeof(readBackVvsBuf));
                MutableByteSpan readBackVvsSpan{ readBackVvsBuf };

                EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                              newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVidVerificationStatement,
                              readBackVvsSpan),
                          CHIP_NO_ERROR);
                EXPECT_TRUE(readBackVvsSpan.empty());
            }
        }
    }

    // Update to NodeID 33 without ICAC
    {
        FabricId fabricId               = 44;
        NodeId nodeId                   = 33;
        FabricIndex existingFabricIndex = 1u;

        MutableByteSpan csrSpan{ csrBuf };
        ASSERT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);
        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(false).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan noc = fabric44CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, ByteSpan{},
                                                                         FabricTable::AdvertiseIdentity::No),
                  CHIP_NO_ERROR);
        ASSERT_NE(fabricTable.FindFabricWithIndex(newFabricIndex), nullptr);
        EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetFabricId(), fabricId);
        EXPECT_EQ(fabricTable.FindFabricWithIndex(newFabricIndex)->GetNodeId(), nodeId);
        EXPECT_EQ(fabricTable.CommitPendingFabricData(), CHIP_NO_ERROR);
        EXPECT_EQ(fabricTable.FabricCount(), 1);
    }

    {
        // Set VVSC. Should succeed due to lack of ICAC.
        bool fabricTableWasChanged = false;
        EXPECT_EQ(fabricTable.SetVIDVerificationStatementElements(newFabricIndex, NullOptional, NullOptional,
                                                                  MakeOptional(vvscSpan), fabricTableWasChanged),
                  CHIP_NO_ERROR);
        EXPECT_EQ(fabricTableWasChanged, false);

        // Make sure VVSC was stored.
        {
            uint8_t readBackVvscBuf[kMaxCHIPCertLength];
            memset(&readBackVvscBuf[0], 0x11, sizeof(readBackVvscBuf));
            MutableByteSpan readBackVvscSpan{ readBackVvscBuf };

            EXPECT_EQ(fabricTableHolder.GetOpCertStore().GetVidVerificationElement(
                          newFabricIndex, OperationalCertificateStore::VidVerificationElement::kVvsc, readBackVvscSpan),
                      CHIP_NO_ERROR);
            EXPECT_TRUE(readBackVvscSpan.data_equal(vvscSpan));
        }
    }

    // Fabric Index 1: Try to update node ID to 55 on fabric 44. Must fail due to VVSC present.
    {
        FabricId fabricId = 44;
        NodeId nodeId     = 55;

        MutableByteSpan csrSpan{ csrBuf };
        FabricIndex existingFabricIndex = 1u;
        EXPECT_EQ(fabricTable.AllocatePendingOperationalKey(MakeOptional(existingFabricIndex), csrSpan), CHIP_NO_ERROR);

        EXPECT_EQ(fabric44CertAuthority.SetIncludeIcac(true).GenerateNocChain(fabricId, nodeId, csrSpan).GetStatus(),
                  CHIP_NO_ERROR);
        ByteSpan icac = fabric44CertAuthority.GetIcac();
        ByteSpan noc  = fabric44CertAuthority.GetNoc();

        EXPECT_EQ(fabricTable.UpdatePendingFabricWithOperationalKeystore(existingFabricIndex, noc, icac,
                                                                         FabricTable::AdvertiseIdentity::No),
                  CHIP_ERROR_INCORRECT_STATE);
        fabricTable.RevertPendingFabricData();
        EXPECT_EQ(fabricTable.FabricCount(), 1);
    }
}

TEST_F(TestFabricTable, VidVerificationSigningFailsOnBadInput)
{
    chip::TestPersistentStorageDelegate storage;
    Crypto::P256SerializedKeypair rootKeyForTestSerialized;

    // Setup a known key so that test vectors are also known and validated.
    // DO NOT CHANGE WITHOUT RE-AUDITING THE TEST VECTORS!
    {
        static const uint8_t kTestCert_Root02_PublicKey_Buffer[] = {
            0x04, 0x27, 0x50, 0x0b, 0x20, 0x60, 0x52, 0xce, 0x33, 0x77, 0x6c, 0x63, 0x08, 0x3f, 0x1c, 0xf1, 0x03,
            0x6e, 0xa4, 0xcc, 0x7f, 0xfd, 0x61, 0x7c, 0x17, 0x6d, 0x4c, 0xad, 0xf5, 0x51, 0xbb, 0xb4, 0xb0, 0xd9,
            0x97, 0xca, 0xe5, 0x55, 0xdb, 0xf9, 0xbc, 0xa8, 0x56, 0xe4, 0xcc, 0x7a, 0x8e, 0xde, 0x91, 0xe0, 0xa7,
            0x33, 0xe1, 0x67, 0xc0, 0x41, 0x67, 0xa6, 0xc2, 0xc9, 0xfa, 0x48, 0xf1, 0x4f, 0x0b,
        };
        const ByteSpan kTestCert_Root02_PublicKey{ kTestCert_Root02_PublicKey_Buffer };

        static const uint8_t kTestCert_Root02_PrivateKey_Buffer[] = {
            0xf9, 0xb7, 0x22, 0x87, 0xd6, 0xa4, 0x8d, 0xd5, 0x10, 0x93, 0xc4, 0x12, 0xe6, 0x1b, 0x43, 0xaf,
            0xe1, 0x22, 0x15, 0x28, 0x96, 0x71, 0xbf, 0x33, 0x96, 0xff, 0x97, 0xf6, 0xb6, 0x21, 0xb2, 0xac,
        };
        const ByteSpan kTestCert_Root02_PrivateKey{ kTestCert_Root02_PrivateKey_Buffer };

        memcpy(rootKeyForTestSerialized.Bytes(), kTestCert_Root02_PublicKey.data(), kTestCert_Root02_PublicKey.size());
        memcpy(rootKeyForTestSerialized.Bytes() + kTestCert_Root02_PublicKey.size(), kTestCert_Root02_PrivateKey.data(),
               kTestCert_Root02_PrivateKey.size());
        EXPECT_SUCCESS(rootKeyForTestSerialized.SetLength(rootKeyForTestSerialized.Capacity()));
    }

    // Initialize a fabric table.
    ScopedFabricTable fabricTableHolder;
    EXPECT_EQ(fabricTableHolder.Init(&storage), CHIP_NO_ERROR);
    FabricTable & fabricTable = fabricTableHolder.GetFabricTable();

    EXPECT_EQ(fabricTable.FabricCount(), 0);

    // Add a basic fabric, don't care about NodeID.
    constexpr FabricId kFabricId1 = 0x1122334455667788;
    constexpr uint16_t kVendorId1 = 0xFFF1u;
    FabricIndex kFabricIndex1     = 1u;

    {
        FabricIndex fabricIndex1 =
            CreateBasicFabricForVidVerification(fabricTable, rootKeyForTestSerialized, kFabricId1, kVendorId1);
        ASSERT_EQ(fabricIndex1, kFabricIndex1);
        ASSERT_EQ(fabricTable.FabricCount(), 1u);
    }

    const uint8_t kAttestationChallenge[16] = {
        0x90, 0x4b, 0x82, 0xe6, 0x6e, 0x98, 0x57, 0x85, 0xb4, 0xa3, 0xff, 0x18, 0xc2, 0x59, 0x47, 0xf3,
    };
    ByteSpan kAttestationChallengeSpan{ kAttestationChallenge };

    const uint8_t kClientChallenge[32] = {
        0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0,
        0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
    };
    ByteSpan kClientChallengeSpan{ kClientChallenge };

    // Non-existent fabricIndex should fail.
    {
        FabricTable::SignVIDVerificationResponseData responseData;

        FabricIndex kNonExistentFabricIndex = 123u;
        ASSERT_EQ(fabricTable.SignVIDVerificationRequest(kNonExistentFabricIndex, kClientChallengeSpan, kAttestationChallengeSpan,
                                                         responseData),
                  CHIP_ERROR_INVALID_ARGUMENT);
    }

    // Invalid attestationChallenge size should fail. Only check too small as the underlying data copied will be caught by just the
    // signature failing.
    {
        FabricTable::SignVIDVerificationResponseData responseData;

        ByteSpan attestationChallengeTooSmall{ kAttestationChallengeSpan.data(), kAttestationChallengeSpan.size() - 1 };
        ASSERT_EQ(
            fabricTable.SignVIDVerificationRequest(kFabricIndex1, kClientChallengeSpan, attestationChallengeTooSmall, responseData),
            CHIP_ERROR_INVALID_ARGUMENT);
    }

    // Invalid client challenge size should fail.
    {
        FabricTable::SignVIDVerificationResponseData responseData;

        ByteSpan clientChallengeTooSmall{ kClientChallengeSpan.data(), kClientChallengeSpan.size() - 1 };
        ASSERT_EQ(
            fabricTable.SignVIDVerificationRequest(kFabricIndex1, clientChallengeTooSmall, kAttestationChallengeSpan, responseData),
            CHIP_ERROR_INVALID_ARGUMENT);

        uint8_t clientChallengeBigBuffer[Crypto::kVendorIdVerificationClientChallengeSize + 1];
        ByteSpan clientChallengeTooBig{ clientChallengeBigBuffer };
        ASSERT_EQ(
            fabricTable.SignVIDVerificationRequest(kFabricIndex1, clientChallengeTooBig, kAttestationChallengeSpan, responseData),
            CHIP_ERROR_INVALID_ARGUMENT);
    }
}

} // namespace
